Swift - как динамически изменить размер UIView в зависимости от размера кадра видеоплеера - PullRequest
0 голосов
/ 31 мая 2019

У меня есть видеоплеер и UIView с наложением, которое я использую для Gesture Recognition в области видео. Я делаю это, чтобы вы могли нажать на другую область видео для воспроизведения, перемотки и выполнения других функций.

В данный момент это работает нормально в альбомной ориентации, но если я поверну устройство в портретную ориентацию. UIView не изменяет размер, но проигрыватель видео делает. Я пытался использовать ограничения программно, но не могу понять, как получить доступ к якору NSLayout видеоплеера.

У меня есть функция, которая извлекает размер видеокадра, который я в настоящее время использую, чтобы установить UIView на размер видео плеера.

Приведенный ниже код можно просто скопировать в проект, чтобы воспроизвести видео и показать UIView, который я хочу настроить. Вам просто нужно добавить UIView в раскадровку UIViewController. Код не содержит часть GestureRecogniser.

import UIKit
import AVFoundation

class ViewController: UIViewController {

let controlContainerView: UIView = {
    let view = UIView()
    view.backgroundColor = UIColor(white: 0, alpha: 1)
    return view
}()

let activityIndicatorView: UIActivityIndicatorView = {
    let aiv = UIActivityIndicatorView(style: .whiteLarge)
    aiv.translatesAutoresizingMaskIntoConstraints = false
    aiv.startAnimating()
    return aiv
}()

@IBOutlet weak var videoContainer: UIView!
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
var tapRegionBounds: UIView!
var tapRegionAreaCreated: Bool = false

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let urlString = "https://www.radiantmediaplayer.com/media/bbb-360p.mp4"
    videoPlayer(filename: urlString)
    player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)

}

override func viewDidAppear(_ animated: Bool) {
    self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
    updateTapRegions()
}

func videoPlayer(filename: String){
    let item = AVPlayerItem(url: URL(string: filename)!)
    player = AVPlayer(playerItem: item)
    playerLayer = AVPlayerLayer(player: player)
    playerLayer?.videoGravity = .resizeAspect
    playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
    videoContainer.layer.addSublayer(playerLayer!)
    player?.play()
}


override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "currentItem.loadedTimeRanges" {
        activityIndicatorView.stopAnimating()
        controlContainerView.backgroundColor = .clear
    }
}

func videoFrame() -> CGRect? {
    guard let layer = playerLayer
        else { return nil }
    let frame = layer.videoRect
    if frame.height > 0.0 {
        let frameInWindow = layer.convert(frame, to: nil)
        return view.convert(frameInWindow, from: nil)
    } else {
        return nil
    }
}

//THE CODE BELOW KIND OF WORKS TO SHOW HOW I WANT IT TO WORK BUT WITHOUT HAVING TO DELETE THE VIEW EACH TIME
func updateTapRegions() {
    guard let video = videoFrame() else { return }
    let container = videoContainer.bounds

    if tapRegionAreaCreated == true {
        tapRegionBounds.removeFromSuperview()
        tapRegionAreaCreated = false
    }

    tapRegionBounds = UIView()
    tapRegionBounds.frame = CGRect(x: video.minX, y: video.minY, width: container.width, height: video.height)
    tapRegionBounds?.alpha = 0.5
    tapRegionBounds.backgroundColor = UIColor.red

    if tapRegionAreaCreated == false {
        view.addSubview(tapRegionBounds)
        tapRegionAreaCreated = true
    }
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { (context) in
    }) { (context) in
        self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
        self.updateTapRegions()
    }
}

}

Я пытался обновить кадр в viewWillLayoutSubviews(), но ничего не изменилось. Я знаю, что моя функция updateTapRegions не правильная, но если бы кто-нибудь мог указать мне правильное направление, это было бы здорово, спасибо.

Ответы [ 2 ]

2 голосов
/ 03 июня 2019

К сожалению, вы не сказали в своем вопросе, как вы изменяете размер контейнера видео, от которого все это зависит.Но здесь есть возможность.Я использовал автоматический макет, чтобы получить эти результаты в портретной и альбомной ориентации:

enter image description here

enter image description here

Теперь предположим, что у нас есть подслой (AVPlayerLayer) и подпредставление (представление жестом касания).Ну, можно изменить размер подпредставления, чтобы он автоматически подходил с помощью autolayout!Таким образом, остается только слой игрока;размер не изменяется автоматически с помощью autolayout, но вы можете изменить его размер вручную в viewDidLayoutSubviews.

0 голосов
/ 03 июня 2019

Наконец, я решил это, переместив код из функции перехода (даже если размер проигрывателя видео изменился правильно) в viewDidLayoutSubviews, который затем работал нормально.Также мне больше не нужно создавать и удалять вид каждый раз, когда устройство меняет ориентацию.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)

    guard let video = videoFrame() else { return }

    if tapRegionBounds != nil {
         self.tapRegionBounds.frame = CGRect(x: video.minX, y: video.minY, width: video.width, height: video.height)
    }
}
...