【问题标题】:AVPlayer video disappears when navigating 'back' in Swift在 Swift 中导航“返回”时,AVPlayer 视频消失
【发布时间】:2021-04-05 16:13:44
【问题描述】:

导航到子视图时,我已将其设置为自动播放视频。在视频的底部,有一组指向相关内容的链接。单击其中一个时,会将一个新视图推送到堆栈中,并开始播放不同的视频。

使用自动生成的“

我已尝试更新 CGRect 帧,使用 onAppear 重新初始化视频播放器,也遵循了 here 的建议。

到目前为止,似乎没有任何效果。这是我用于实际视频播放器的代码(改编自 Chris Mash 的网站):

import SwiftUI
import AVKit
import UIKit
import AVFoundation

let playerLayer = AVPlayerLayer()
class PlayVideo: UIView {

    init(frame: CGRect, url: URL) {
        super.init(frame: frame)
        
        // Create the video player using the URL passed in.
        let player = AVPlayer(url: url)
        player.volume = 100 // Will play audio if you don't set to zero
        player.play() // Set to play once created
        
        // Add the player to our Player Layer
        playerLayer.player = player
        playerLayer.videoGravity = .resizeAspectFill // Resizes content to fill whole video layer.
        playerLayer.backgroundColor = UIColor.black.cgColor
        
        layer.addSublayer(playerLayer)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
   
    static func pauseVideo() {
        playerLayer.player?.pause()
    }
}


struct ViewVideo: UIViewRepresentable {
    
    var videoURL:URL
    var previewLength:Double?
    
    func makeUIView(context: Context) -> UIView {
        return PlayVideo(frame: .zero, url: videoURL)
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        
    }
}

这是从主视图调用的,使用: ViewVideo(videoURL: videoURL)

我能想到的唯一解决方法是禁用后退按钮并强制用户每次都返回主视图。这是一个糟糕的选择,我希望有人能在这里提供一些有用的建议。谢谢 -

【问题讨论】:

标签: ios swift avfoundation avplayer avkit


【解决方案1】:

如果我理解正确,当您导航到新视图时,您会播放不同的视频。所以你创建一个新的PlayVideo 视图?

那么问题是你的playerLayer 是一个静态属性。新视图将在playerLayer 中设置一个新播放器并替换旧播放器。同样,如果您暂停一个播放器,则两个播放器都会暂停。此外,将播放器作为子图层添加到新视图会将其从旧视图中移除。

您需要 playerLayer 作为视图的本地属性。或者至少为您要播放的每个视频提供AVPlayerLayer。然后,您需要一种机制来暂停/重新启动每个视频,当它变得可见时。例如通过实现viewWillAppear。当您导航回视图/视图变为可见时,始终会调用此方法。

【讨论】:

  • 超级!这正是问题所在。我修改了代码并将修改发布为正确答案。感谢您的所有帮助。
【解决方案2】:

感谢 @dominik-105 帮助解决这个问题。我能够使用提出的建议解决问题。

具体来说,我删除了 playerLayer 的全局定义,而是将其作为局部变量放置在我的主视图调用中:

var playerLayer = AVPlayerLayer()

然后我使用 playerLayer: playerLayer 标签调用 ViewVideo,然后 ViewVideo 使用 playerLayer:AVPlayerLayer 作为初始化的一部分调用 PlayVideo。

有趣的是,这会导致我用来定义视频框大小的覆盖布局功能出现问题。我现在直接在 init 中定义框架,并删除了旧的覆盖函数代码。完整代码如下:

import SwiftUI
import AVKit
import UIKit
import AVFoundation


class PlayVideo: UIView {
    init(frame: CGRect, url: URL, playerLayer: AVPlayerLayer, width: CGFloat, height: CGFloat) {
        super.init(frame: frame)
        
        // Create the video player using the URL passed in.
        let player = AVPlayer(url: url)
        player.volume = 100 // Will play audio if you don't set to zero
        player.play() // Set to play once created
        
        // Add the player to our Player Layer
        playerLayer.player = player
        playerLayer.videoGravity = .resizeAspectFill // Resizes content to fill whole video layer.
        playerLayer.backgroundColor = UIColor.black.cgColor

        playerLayer.player?.actionAtItemEnd = .pause
        layer.addSublayer(playerLayer)
        playerLayer.frame = CGRect(x: 0, y: 0, width: width, height: height)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

}


struct ViewVideo: UIViewRepresentable {
    var videoURL:URL
    var playerLayer: AVPlayerLayer
    var width: CGFloat
    var height: CGFloat
    
    func makeUIView(context: Context) -> UIView {
        return PlayVideo(frame: .zero, url: videoURL, playerLayer: playerLayer, width: width, height: height)
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        
    }
}

我使用 GeometryReader 来定义盒子的大小,然后将宽度和高度传递给结构体,结构体将其传递给类。

【讨论】: