7

我正在开发一个聊天机器人,其中不同类型的响应来自服务器,我使用UICollectionView聊天屏幕中的单元格显示响应。根据服务器响应呈现不同类型的单元格。当服务器响应播放视频时,我正在展示包含 youtube 播放器的单元格。我正在使用https://github.com/kieuquangloc147/YouTubePlayer-Swift。问题是当我滚动聊天屏幕(collectionView)时,youtube 播放器一次又一次地打开。有时它会阻止所有 UI 元素并停止滚动。我尝试了不同的方法,但无法解决它。这是代码 PlayerView::

import UIKit

class PlayerView: UIView, YouTubePlayerDelegate {

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

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

    // youtube player
    lazy var youtubePlayer: YouTubePlayerView = {
        let viewFrame = UIScreen.main.bounds
        let player = YouTubePlayerView(frame: CGRect(x: 0, y: 0, width: viewFrame.width - 16, height: viewFrame.height * 1/3))
        player.delegate = self
        return player
    }()

    // used as an overlay to dismiss the youtube player
    let blackView = UIView()

    // youtube player loader
    lazy var playerIndicator: UIActivityIndicatorView = {
        let indicator = UIActivityIndicatorView()
        indicator.activityIndicatorViewStyle = .whiteLarge
        indicator.hidesWhenStopped = true
        return indicator
    }()

    // shows youtube player
    func addYotubePlayer() {
        if let window = UIApplication.shared.keyWindow {
            blackView.frame = window.frame
            self.addSubview(blackView)
            blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
            let tap = UITapGestureRecognizer(target: self, action: #selector(handleDismiss))
            tap.numberOfTapsRequired = 1
            tap.cancelsTouchesInView = false
            blackView.addGestureRecognizer(tap)


            let centerX = UIScreen.main.bounds.size.width / 2
            let centerY = UIScreen.main.bounds.size.height / 2

            blackView.addSubview(playerIndicator)
            playerIndicator.center = CGPoint(x: centerX, y: centerY)
            playerIndicator.startAnimating()

            blackView.addSubview(youtubePlayer)
            youtubePlayer.center = CGPoint(x: centerX, y: centerY)

            blackView.alpha = 0
            youtubePlayer.alpha = 0

            UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
                self.blackView.alpha = 1
                self.youtubePlayer.alpha = 1
            }, completion: nil)
        }
    }

    func play(_ videoID: String) {
        youtubePlayer.loadVideoID(videoID)
    }

    @objc func handleDismiss() {
        blackView.removeFromSuperview()
        UIApplication.shared.keyWindow?.viewWithTag(24)?.removeFromSuperview()
        UIApplication.shared.keyWindow?.removeFromSuperview()
    }

    func playerReady(_ videoPlayer: YouTubePlayerView) {
        self.playerIndicator.stopAnimating()
    }

    func playerStateChanged(_ videoPlayer: YouTubePlayerView, playerState: YouTubePlayerState) {
    }

    func playerQualityChanged(_ videoPlayer: YouTubePlayerView, playbackQuality: YouTubePlaybackQuality) {
    }

}

YouTubePlayerCell(我在collectionView母鸡服务器响应视频时提出):

import UIKit

class YouTubePlayerCell: ChatMessageCell {

    var player: PlayerView = PlayerView(frame: UIScreen.main.bounds)

    override func setupViews() {
        super.setupViews()
        setupCell()
    }

    func setupCell() {
        messageTextView.frame = CGRect.zero
        textBubbleView.frame = CGRect.zero
    }

    func loadVideo(with videoID: String) {
        player.tag = 24
        UIApplication.shared.keyWindow?.addSubview(player)
        player.play(videoID)
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        player.removeFromSuperview()
        UIApplication.shared.keyWindow?.viewWithTag(24)?.removeFromSuperview()
    }

}

以下是我介绍YouTubePlayerCellincellForItemAt方法的方式UICollectionView

let message = messages[indexPath.row]
    if message.actionType == ActionType.video_play.rawValue {
                if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ControllerConstants.youtubePlayerCell, for: indexPath) as? YouTubePlayerCell {
                    self.resignResponders()
                    if let videoId = message.videoData?.identifier {
                        cell.loadVideo(with: videoId)
                    }
                    return cell
                }
            }

完整的源代码可以在这里找到:https ://github.com/imjog/susi_iOS/tree/ytplayer

4

3 回答 3

1

我可以在下面的代码中看到

if let videoId = message.videoData?.identifier {
                        cell.loadVideo(with: videoId)
                    }

您正在调用loadVideo方法,该方法负责显示播放器。因此,在滚动时,您正在重用单元格,它会调用loadVideo方法并呈现播放器。因此解决方案是默认情况下在显示单元格时不开始播放视频,在单元格视频叠加层上提供播放/暂停按钮,然后单击按钮开始播放视频。如果我的分析是错误的,请告诉我,你有什么确切的问题。

于 2018-07-16T06:21:59.280 回答
0

为什么每次必须播放视频时都将播放器添加为子视图?我的建议是,当您在整个屏幕上添加播放器视图时,您可以只拥有一个视图实例并仅添加一次(可能在开始时)并将其隐藏。要播放视频,只需取消隐藏播放器并加载视频。

相反,最佳做法是为 Youtube Player 提供一个 View 控制器,并在每次需要播放时向其显示视频 ID,然后在完成后关闭。

于 2018-07-20T06:16:36.067 回答
0

感谢您的回答。我通过这种方式解决了这个问题:

我现在不是在单元格上显示 Player 的设置,而是在单元格中添加一个缩略图,并在缩略图视图上添加一个按钮,这样每当用户单击播放按钮时,它都会打开一个新控制器(以前我在 中显示UIWindow)并将其显示为modalPresentationStyle通过overFullScreen使用协议,因为单元格不能呈现 ViewController。

协议:(在 YouTubePlayerCell 类中)

protocol PresentControllerDelegate: class {
    func loadNewScreen(controller: UIViewController) -> Void
}

决赛YouTubePlayer.swift

import UIKit
import Kingfisher

protocol PresentControllerDelegate: class {
    func loadNewScreen(controller: UIViewController) -> Void
}

class YouTubePlayerCell: ChatMessageCell {

    weak var delegate: PresentControllerDelegate?

    var message: Message? {
        didSet {
            addThumbnail()
        }
    }

    lazy var thumbnailView: UIImageView = {
        let imageView = UIImageView()
        imageView.image = ControllerConstants.Images.placeholder
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        imageView.layer.cornerRadius = 15
        imageView.isUserInteractionEnabled = true
        return imageView
    }()

    lazy var playButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(ControllerConstants.Images.youtubePlayButton, for: .normal)
        button.addTarget(self, action: #selector(playVideo), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func setupViews() {
        super.setupViews()
        setupCell()
        prepareForReuse()
    }

    func setupCell() {
        messageTextView.frame = CGRect.zero
        textBubbleView.frame = CGRect(x: 8, y: 0, width: 208, height: 158)
        textBubbleView.layer.borderWidth = 0.2
        textBubbleView.backgroundColor = .white
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        thumbnailView.image = nil
    }

    func addThumbnail() {
        textBubbleView.addSubview(thumbnailView)
        textBubbleView.addConstraintsWithFormat(format: "H:|-4-[v0]-4-|", views: thumbnailView)
        textBubbleView.addConstraintsWithFormat(format: "V:|-4-[v0]-4-|", views: thumbnailView)
        self.downloadThumbnail()
        self.addPlayButton()
    }

    func addPlayButton() {
        thumbnailView.addSubview(playButton)
        playButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
        playButton.widthAnchor.constraint(equalToConstant: 44).isActive = true
        playButton.centerXAnchor.constraint(equalTo: thumbnailView.centerXAnchor).isActive = true
        playButton.centerYAnchor.constraint(equalTo: thumbnailView.centerYAnchor).isActive = true
    }

    func downloadThumbnail() {
        if let videoID = message?.videoData?.identifier {
            let thumbnailURLString = "https://img.youtube.com/vi/\(videoID)/default.jpg"
            let thumbnailURL = URL(string: thumbnailURLString)
            thumbnailView.kf.setImage(with: thumbnailURL, placeholder: ControllerConstants.Images.placeholder, options: nil, progressBlock: nil, completionHandler: nil)
        }
    }

    @objc func playVideo() {
        if let videoID = message?.videoData?.identifier {
            let playerVC = PlayerViewController(videoID: videoID)
            playerVC.modalPresentationStyle = .overFullScreen
            delegate?.loadNewScreen(controller: playerVC)
        }
    }

}

CollectionViewController中的委托实现:

extension ChatViewController: PresentControllerDelegate {

    func loadNewScreen(controller: UIViewController) {
        self.present(controller, animated: true, completion: nil)
    }

}

最终源代码可以在这里找到:https ://github.com/fossasia/susi_iOS/pull/372

于 2018-07-21T06:49:37.077 回答