In SwiftUI, how can I add a video on loop as a fullscreen background image?

回眸只為那壹抹淺笑 提交于 2020-04-30 06:38:30

问题


I have a video thats around 10 seconds long that I'd like to play on a loop as a fullscreen background image in one of my SwiftUI Views. How can I implement this?

First idea was working with Swift's import AVFoundation, but not sure if this is the right path.


回答1:


You can use the AV family of frameworks and UIViewRepresentable to do this:

import SwiftUI
import AVKit
import AVFoundation

struct PlayerView: UIViewRepresentable {
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
    }

    func makeUIView(context: Context) -> UIView {
        return PlayerUIView(frame: .zero)
    }
}

In order for the video to loop I have added an observer and set the actionAtItemEnd to .none to support looping.

When the video reaches the end it will execute the playerItemDidReachEnd(...) method and seek to the beginning of the video and keep looping.

The example points to a remote video URL. If you want to point to a file within your application you can use Bundle.main.url to do so instead:

if let fileURL = Bundle.main.url(forResource: "IMG_2770", withExtension: "MOV") {
    let player = AVPlayer(url: fileURL)
    // ...
}

class PlayerUIView: UIView {
    private let playerLayer = AVPlayerLayer()

    override init(frame: CGRect) {
        super.init(frame: frame)

        let url = URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!
        let player = AVPlayer(url: url)
        player.actionAtItemEnd = .none
        player.play()

        playerLayer.player = player
        playerLayer.videoGravity = .resizeAspectFill

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(playerItemDidReachEnd(notification:)),
                                               name: .AVPlayerItemDidPlayToEndTime,
                                               object: player.currentItem)

        layer.addSublayer(playerLayer)
    }

    @objc func playerItemDidReachEnd(notification: Notification) {
        if let playerItem = notification.object as? AVPlayerItem {
            playerItem.seek(to: .zero, completionHandler: nil)
        }
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                PlayerView()
                    .edgesIgnoringSafeArea(.all)
            }
        }
    }
}



回答2:


SwiftUI

As someone completely new to swift and for anyone who doesn't want to spend hours debugging this like I did. My use case was trying to create a login screen with a video playing in the background. I was struggling with the looping not working and then with the video stopping after a few seconds and starting again after the duration. This works for me.

Add a new view:

import SwiftUI
import AVKit
import AVFoundation

struct WelcomeVideo: View {
    var body: some View {
        WelcomeVideoController()
    }
}

struct WelcomeVideo_Previews: PreviewProvider {
    static var previews: some View {
        WelcomeVideo()
    }
}

final class WelcomeVideoController : UIViewControllerRepresentable {
    var playerLooper: AVPlayerLooper?

    func makeUIViewController(context: UIViewControllerRepresentableContext<WelcomeVideoController>) ->
        AVPlayerViewController {
            let controller = AVPlayerViewController()

            guard let path = Bundle.main.path(forResource: "welcome", ofType:"mp4") else {
                debugPrint("welcome.mp4 not found")
                return controller
            }

            let asset = AVAsset(url: URL(fileURLWithPath: path))
            let playerItem = AVPlayerItem(asset: asset)
            let queuePlayer = AVQueuePlayer()
            // OR let queuePlayer = AVQueuePlayer(items: [playerItem]) to pass in items

            playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)
            queuePlayer.play()
            controller.player = queuePlayer

            return controller
        }

    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<WelcomeVideoController>) {
    }
}

Then attach it to a view background:

.background(WelcomeVideo())

NOTE:

  1. Make sure your video is imported to your project
  2. Update the name of the video to what you need or refactor slightly to pass it in

Cheers!



来源:https://stackoverflow.com/questions/59937756/in-swiftui-how-can-i-add-a-video-on-loop-as-a-fullscreen-background-image

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!