Видео AVPlayer исчезает при переходе «назад» в Swift

При переходе к подвиду я настроил его так, чтобы видео воспроизводилось автоматически. Внизу видео есть группа ссылок, которые ведут к соответствующему контенту. При нажатии на один из них в стек помещается новый вид, и начинается воспроизведение другого видео.

Проблема возникает при использовании автоматически сгенерированной кнопки «‹ Назад», чтобы вернуться к предыдущему просмотру (в котором было другое видео). Этим исходным видом можно управлять с помощью элементов управления проигрывателя, но на экране ничего не отображается.

Я попытался обновить кадр CGRect, использовать onAppear для повторной инициализации видеоплеера, а также последовал совету здесь.

Пока ничего не работает. Вот код, который я использую для реального видеоплеера (адаптировано с веб-сайта Криса Маша):

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)

Единственная работа, которую я могу придумать, - это отключить кнопку «Назад» и заставить пользователя каждый раз возвращаться к основному виду. Это ужасный вариант, и я надеюсь, что кто-то даст здесь полезный совет. Спасибо -


person FontFamily    schedule 28.12.2020    source источник
comment
Я попробовал рекомендации здесь: developer.apple.com/forums/thread/116120. Это тоже не работает. Проблема также возникает при переходе в полноэкранный режим и обратно.   -  person FontFamily    schedule 29.12.2020


Ответы (2)


Если я правильно понимаю, вы воспроизводите другое видео, когда переходите к новому представлению. Итак, вы создаете новое представление PlayVideo?

Тогда проблема в том, что ваш playerLayer является статическим свойством. Новый вид установит нового игрока в playerLayer и заменит старого. Точно так же, если вы приостановите воспроизведение одного игрока, оба будут приостановлены. Кроме того, добавление игрока в качестве подслоя в новый вид удалит его из старого вида.

Вам нужно playerLayer как локальное свойство для вашего представления. Или хотя бы AVPlayerLayer за каждое видео, которое вы хотите воспроизвести. Затем вам нужен механизм для приостановки/перезапуска каждого видео, когда оно становится видимым. Например, реализуя viewWillAppear. Этот метод всегда вызывается, когда вы возвращаетесь к представлению/представлению, которое становится видимым.

person Dominik 105    schedule 29.12.2020
comment
Супер! Именно в этом и была проблема. Я пересмотрел код и опубликовал исправление как правильный ответ. Спасибо тебе за помощь. - person FontFamily; 29.12.2020

Спасибо @dominik-105 за помощь в этом вопросе. Я смог решить проблему, используя предложения, которые были сделаны.

В частности, я удалил глобальное определение playerLayer и вместо этого поместил его как локальную переменную в мой вызов основного представления:

var playerLayer = AVPlayerLayer()

Затем я вызываю ViewVideo с тегом playerLayer: playerLayer, а затем ViewVideo вызывает PlayVideo с тегом playerLayer:AVPlayerLayer как часть инициализации.

Интересно, что это приводит к проблемам с функцией переопределения макета, которую я использовал для определения размера видеобокса. Теперь я определяю кадр непосредственно в инициализации и удаляю старый код функции переопределения. Полный код теперь:

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 для определения размера блока, а затем передаю ширину и высоту в структуру, которая передает их классу.

person FontFamily    schedule 29.12.2020