使用 JWVideoView 在 UICollectionViewCell 上崩溃 - Swift

Crash on UICollectionViewCell with JWVideoView - Swift

一个ViewController有一个UICollectionView。其中一个单元格包含 JWVideoView。该应用经常在此单元格中的 prepareForReuse 崩溃。

日志中没有有价值的信息。所以我无法找出崩溃的原因。

我创建了一个演示崩溃的项目示例。你可以找到它https://github.com/fuxlud/JWExample 如果去掉cellvideoView之间的link,就不会发生崩溃。

import UIKit

class VideoArticleElementCollectionViewCell: UICollectionViewCell {

    // MARK: - Properties

    public var imageURL: String? { didSet { videoView?.imageURL = imageURL } }
    public var videoId: String? { didSet { videoView?.videoId = videoId } }

    @IBOutlet private var videoView: JWVideoView?

    // MARK: - Reuse

    override func prepareForReuse() {
        super.prepareForReuse() // Crashing here! (Thread 1: EXC_BAD_ACCESS (code=1, address=0x7e8))

        videoView?.stopPlayingVideo()
    }

    deinit {

        videoView?.stopPlayingVideo()
    }
}








import UIKit

class JWVideoView: UIView, JWPlayerDelegate {

    // MARK: Properties

    public var imageURL: String?
    public var videoId: String? { didSet { setupPlayer() } }

    private var jwPlayer: JWPlayerController?
    private let jwPlayerURL = "https://content.jwplatform.com/manifests/"
    private var didPause = false

    // MARK: - Initialization

    override init(frame: CGRect) {

        super.init(frame: frame)
        setup()
    }

    convenience init() {

        self.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {

        super.init(coder: aDecoder)
        setup()
    }

    // MARK: - Setup

    private func setup() {}

    private func setupPlayer() {



            guard let videoId = self.videoId else { return }

            let playerURL = jwPlayerURL + videoId + ".m3u8"

            let configuration: JWConfig = JWConfig(contentURL: playerURL)
            configuration.controls = true
            configuration.autostart = true
//            configuration.premiumSkin = JWPremiumSkinGlow
            configuration.image = imageURL

            jwPlayer = JWPlayerController(config: configuration)

            if let player = jwPlayer {

                player.forceFullScreenOnLandscape = true
                player.forceLandscapeOnFullScreen = true
                player.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
                player.view?.frame = bounds
                player.delegate = self
                player.volume = 0.0
                if let view = player.view { addSubview(view) }
            }

    }

    // MARK: - Orientation

    private func enableAllOrientation(enable: Bool) {

        if let delegate = UIApplication.shared.delegate as? AppDelegate {

//            delegate.shouldEnableLandscape = enable
        }
    }

    // MARK: API

    public func stopPlayingVideo() {

        enableAllOrientation(enable: false)

        if jwPlayer != nil {

            jwPlayer!.stop()
        }
    }

    // MARK: - JWPlayerDelegate

    internal func onFullscreen(_ status: Bool) {

        if status == false {

            let value = UIInterfaceOrientation.portrait.rawValue
            UIDevice.current.setValue(value, forKey: "orientation")
        }
    }

    internal func onPlayAttempt() {

        if jwPlayer != nil {

            enableAllOrientation(enable: true)


        }
    }

    internal func onPlay(_ oldValue: String) {

        if didPause {

            didPause = false
        }
    }

    internal func onPause(_ oldValue: String) {

        didPause = true

    }

    internal func onComplete() {

    }

}

根据您的示例项目,在您的 JWVideoView class 中发现了以下问题:每次您设置 videoId 属性 时,它都会再次启动 jwPlayer,并再次将该视图读取到堆栈中。

1.解决方案(去掉playerView,设置player为nil):

  private func setupPlayer() {

  jwPlayer?.view?.removeFromSuperview()
  jwPlayer = nil

  guard let videoId = self.videoId else { return }

  let playerURL = jwPlayerURL + videoId + ".m3u8"

  let configuration: JWConfig = JWConfig(contentURL: playerURL)
  configuration.controls = true
  configuration.autostart = true
  configuration.image = imageURL

  jwPlayer = JWPlayerController(config: configuration)
  jwPlayer?.forceFullScreenOnLandscape = true
  jwPlayer?.forceLandscapeOnFullScreen = true
  jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
  jwPlayer?.view?.frame = bounds
  jwPlayer?.delegate = self
  jwPlayer?.volume = 0.0

  if let view = jwPlayer?.view {
      addSubview(view)
  }

}

2。解决方案(保留播放器和视图实例并重置播放器的配置)

  private func setupPlayer() {

  guard let videoId = self.videoId else { return }

  let playerURL = jwPlayerURL + videoId + ".m3u8"

  let configuration: JWConfig = JWConfig(contentURL: playerURL)
  configuration.controls = true
  configuration.autostart = true
  configuration.image = imageURL

  if jwPlayer == nil {

      jwPlayer = JWPlayerController(config: configuration)
      jwPlayer?.forceFullScreenOnLandscape = true
      jwPlayer?.forceLandscapeOnFullScreen = true
      jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
      jwPlayer?.view?.frame = bounds
      jwPlayer?.delegate = self
      jwPlayer?.volume = 0.0

      if let view = jwPlayer?.view {
          addSubview(view)
      }
  }else{
    //reset the configuration of the player here. but i dont now how this is possible with jwPlayer
  }

}