изменение размера ячейки при повороте - PullRequest
0 голосов
/ 12 октября 2018

Я изменяю размер ячейки на iPhone X, чтобы встроить экземпляр AVPlayerController.Когда я меняю ориентацию с книжной на альбомную, кажется, что размер кадра меняется.Я получаю элементы управления (полный экран + громкость), перекрывающие заголовок и заголовок.Вы бы порекомендовали решение, отличное от:

self.frame.insetBy

Вот демонстрация того, как это выглядит: iphone x demo

import UIKit
import AVKit


class VGMediaPlayerCell: VGBaseCell {

    let statusBarHeight: CGFloat = 20
    let contentOffset: CGFloat = 50
    static let vgReuseIdentifier = "VGMediaPlayerCell"
    static var playerIsPlaying: Bool = false
    var toggleHeaderVisibility: Bool = false
    public weak var delegate: VGMediaPlayerCellDelegate?

    var moviePlayerController = AVPlayerViewController()

    var waitingIndicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.whiteLarge)
    var containerView = UIView()
    var messageLabel = UILabel()

    var needAutoPlay: Bool = false
    var isLoaded: Bool = false


    var asset: AVAsset?
    var isReadyForDisplayObserver: NSKeyValueObservation?

    var content: VGContent?
    let deviceOrientation = UIDevice.current.orientation

    //player settings
    @objc var player: AVPlayer?
    var PlayerViewConroller: AVPlayerViewController?


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

        isReadyForDisplayObserver = moviePlayerController.observe(\.isReadyForDisplay) { [weak self] (_, _) in
            guard let `self` = self else {
                return
            }

            // When the first frame of the video is loaded, we dismiss the waiting indicator.
            DispatchQueue.main.async {
                if self.moviePlayerController.isReadyForDisplay {
                    self.waitingStateActive(isActive: false)
                }
            }
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        self.isLoaded = false
        needAutoPlay = false
        moviePlayerController.player = nil
        content = nil
        asset = nil
        player = nil
        contextualLabel.font = nil
        messageLabel.text = nil
    }

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

    // MARK: - View creation
    func setupContainerView() {
        addSubview(containerView)
        containerView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            containerView.leftAnchor.constraint(equalTo: leftAnchor),
            containerView.rightAnchor.constraint(equalTo: rightAnchor),
            containerView.topAnchor.constraint(equalTo: topAnchor),
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }

    func setupMessageLabel() {
        addSubview(messageLabel)
        messageLabel.textAlignment = .center
        messageLabel.textColor = .white
        messageLabel.numberOfLines = 2
        messageLabel.isHidden = true
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            messageLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 10),
            messageLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -10),
            messageLabel.heightAnchor.constraint(equalToConstant: 50),
            messageLabel.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }

    func setupWaitingIndicator() {
        addSubview(waitingIndicator)
        waitingIndicator.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            waitingIndicator.centerXAnchor.constraint(equalTo: centerXAnchor),
            waitingIndicator.centerYAnchor.constraint(equalTo: centerYAnchor),
            waitingIndicator.widthAnchor.constraint(equalToConstant: 100),
            waitingIndicator.heightAnchor.constraint(equalToConstant: 100)
        ])
    }


    // MARK: - Utils
    func configurePlayer(with viewModel: VGMediaPlayerViewModel) {

        //to update message label + loader
        updateUI(with: viewModel)

        if viewModel.error == ErrorMessage.noNetwork.rawValue {
            self.stop()
        }

        // Create a new AVPlayer and AVPlayerLayer
        guard let url = URL(string: viewModel.content?.contentURL ?? "") else { return }
        self.player = AVPlayer(url: url)

        // We want video controls so we need an AVPlayerViewController
        PlayerViewConroller = AVPlayerViewController()
        PlayerViewConroller?.player = player
        PlayerViewConroller?.videoGravity = AVLayerVideoGravity.resizeAspect

        insertSubview(avPlayerViewConroller!.view, at: 0)
        PlayerViewConroller!.view.topAnchor.constraint(equalTo: topAnchor).isActive = true
        PlayerViewConroller!.view.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        PlayerViewConroller!.view.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        PlayerViewConroller!.view.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        self.bringSubviewToFront((avPlayerViewConroller?.view!)!)

        if #available(iOS 10.0, *) {
            self.player?.automaticallyWaitsToMinimizeStalling = false
        }
        guard let asset = viewModel.avAsset else { return }

        if !asset.isPlayable {
            DispatchQueue.main.async {
                self.waitingStateActive(isActive: false)
                self.displayError(message: ErrorMessage.noPreview.rawValue)
            }
        }

        DispatchQueue.main.async {
            // Create a new AVAsset from the URL
            let videoAsset = AVAsset(url: url)
            // // Now we need an AVPlayerItem to pass to the AVPlayer
            let videoPlayerItem = AVPlayerItem(asset: videoAsset)
            // // Finally, we set this as the current AVPlayer item
            self.player?.replaceCurrentItem(with: videoPlayerItem)
            if self.needAutoPlay {
                self.player?.play()
            }
            self.isLoaded = true
        }

        //custom insets per device orientation
        // regular from for iphone 8 and downwards
        // custom frame for iphone X and upwards
        if UIDevice().userInterfaceIdiom == .phone {
            switch UIScreen.main.nativeBounds.height {
                //iPhone 5 or 5S or 5C, iPhone 6/6S/7/8, iPhone 6+/6S+/7+/8+
                case 1136,  1334, 1920, 2208:

                    PlayerViewConroller?.view.frame = self.frame

                //iPhone X, Xs, iPhone Xs Max, iPhone Xr
                case 2436, 2688, 1792:
                    if UIApplication.shared.statusBarOrientation.isPortrait {
                        PlayerViewConroller?.view.frame = self.frame.insetBy(dx: 0.0, dy: 50.0)
                    } else if deviceOrientation == .landscapeLeft || deviceOrientation == .landscapeRight {
                        PlayerViewConroller?.view.frame = self.frame.insetBy(dx: 30.0, dy: 30.0)
                    }
            default: break
            }
        } else {
            //for the iPad
            PlayerViewConroller?.view.frame = self.frame
        }


        //Add observer on keypath rate to monitor player's playing status
        if self.toggleHeaderVisibility == true {
            if UIDevice().userInterfaceIdiom == .phone {
                switch UIScreen.main.nativeBounds.height {
                    case 2436, 2688, 1792:
                    player?.addObserver(self, forKeyPath: "rate", options: [.old, .new], context: nil)
                    default : break
                }
            }
        }

        player?.addObserver(self, forKeyPath: "rate", options: [.old, .new], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object as AnyObject? === player {
            if keyPath == "rate" {
                guard let rate = player?.rate  else { return }
                if rate > Float(0.0) {
                    VGMediaPlayerCell.playerIsPlaying = true
                    NotificationCenter.default.post(name: .playerDidStartPlay, object: nil)

                } else {
                    VGMediaPlayerCell.playerIsPlaying = false
                    NotificationCenter.default.post(name: .playerDidStop, object: nil)
                }
            }
        }
    }

    func updateUI(with viewModel: VGMediaPlayerViewModel) {
        messageLabel.isHidden = true

        //indicating waiting state with spinner
        waitingStateActive(isActive: viewModel.isLoading)
    }

    /**
    Cancel asset loading
    */

    func cancelLoading() {
        asset?.cancelLoading()
    }

    /**
    Show an error with a specific message

    - parameter message: A message
    */
    func displayError(message: String) {
        messageLabel.text = message
        messageLabel.isHidden = false
        containerView.isHidden = true
    }

    /**
    Update the waiting indicator state
    - parameter active: A boolean value that indicate if the waiting indicator need to be active or not.
    */

    func waitingStateActive(isActive: Bool) {
        isActive ? waitingIndicator.startAnimating() : waitingIndicator.stopAnimating()
        containerView.isHidden = isActive
    }
}
...