如何从自定义视图过渡到 AVPlayerViewController?
How to transition to AVPlayerViewController from a custom view?
我有一个自定义视图:
class MediaPlayerView: UIView {
var mediaURL: URL? {
didSet {
determineMediaType()
}
}
let videoExtensions = ["mov"]
override init (frame : CGRect) {
super.init(frame : frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func determineMediaType() {
let url = self.mediaURL
let pathExtention = url?.pathExtension
if videoExtensions.contains(pathExtention!) {
print("Movie URL: \(String(describing: url))")
setupVideo(url: url!)
} else {
print("Image URL: \(String(describing: url))")
setupImage(url: url!)
}
}
func setupVideo(url: URL) {
let playButton = UIImage(named: "Play Triangle")
let playButtonView = UIImageView(image: playButton!)
let singleTap = UITapGestureRecognizer(target: self, action: #selector(tapDetected))
playButtonView.addGestureRecognizer(singleTap)
playButtonView.center = self.center
playButtonView.frame.size.width = self.frame.size.width/5
playButtonView.frame.size.height = self.frame.size.height/5
playButtonView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
playButtonView.isUserInteractionEnabled = true
self.addSubview(playButtonView)
}
@objc func tapDetected() {
print("tap!")
let player = AVPlayer(url: self.mediaURL!)
let controller = AVPlayerViewController()
controller.player = player
self.window?.rootViewController?.present(controller, animated: true) {
player.play()
}
}
func setupImage(url: URL) {
let imageView = UIImageView()
imageView.frame = self.bounds
imageView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.addSubview(imageView)
imageView.kf.setImage(with: url)
}
}
但是当我点击播放按钮时,出现以下错误:
Warning: Attempt to present <AVPlayerViewController: 0x7fe8b200b000> on <SweatNet.MainTabBarController: 0x7fe8b4816400> whose view is not in the window hierarchy!
它出现在这一行:self.window?.rootViewController?.present。我认为我调用 rootViewController(似乎是 SweatNet.MainTabBarController)让我感到困惑。我想让它调用 SweatNet.TagViewController。这是包含自定义 MediaPlayerView 的单元格的那个,但我不明白如何获得对此的引用。
试试这个:
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
将你的方法写成:
@objc func tapDetected() {
print("tap!")
let player = AVPlayer(url: self.mediaURL!)
let controller = AVPlayerViewController()
controller.player = player
topViewController().present(controller, animated: true) {
player.play()
}
}
这将使您 return 在层次结构中排在首位 vc。希望对您有所帮助!
我对有关此答案的一些反馈很感兴趣,因为我认为这是我以错误的方式思考 iOS 系统的一个例子。我最终意识到我试图将视图控制器代码放入自定义视图中。查看控制器代码是启动 AVPlayerViewController 的代码。相反,我应该将此逻辑放在我的视图控制器中。
我所做的工作是在视图控制器内部使用 collectionView 委托方法 didSelectItemAt indexPath: IndexPath,其中包含集合视图的视图控制器逻辑,如下所示:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.collectionView {
let post = posts[indexPath.row]
if post.isVideo == true {
self.performSegue(withIdentifier: "playVideo", sender: nil)
} else {
print("is image. might go full screen one day here")
}
}
这有效,我相信它比编写视图的自定义方面来启动 AVPlayerViewController 更好。但也可能这两种方法都有效——我真的对 iOS 开发了解不够,无法做出这样的决定。欢迎任何意见。
我有一个自定义视图:
class MediaPlayerView: UIView {
var mediaURL: URL? {
didSet {
determineMediaType()
}
}
let videoExtensions = ["mov"]
override init (frame : CGRect) {
super.init(frame : frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func determineMediaType() {
let url = self.mediaURL
let pathExtention = url?.pathExtension
if videoExtensions.contains(pathExtention!) {
print("Movie URL: \(String(describing: url))")
setupVideo(url: url!)
} else {
print("Image URL: \(String(describing: url))")
setupImage(url: url!)
}
}
func setupVideo(url: URL) {
let playButton = UIImage(named: "Play Triangle")
let playButtonView = UIImageView(image: playButton!)
let singleTap = UITapGestureRecognizer(target: self, action: #selector(tapDetected))
playButtonView.addGestureRecognizer(singleTap)
playButtonView.center = self.center
playButtonView.frame.size.width = self.frame.size.width/5
playButtonView.frame.size.height = self.frame.size.height/5
playButtonView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
playButtonView.isUserInteractionEnabled = true
self.addSubview(playButtonView)
}
@objc func tapDetected() {
print("tap!")
let player = AVPlayer(url: self.mediaURL!)
let controller = AVPlayerViewController()
controller.player = player
self.window?.rootViewController?.present(controller, animated: true) {
player.play()
}
}
func setupImage(url: URL) {
let imageView = UIImageView()
imageView.frame = self.bounds
imageView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.addSubview(imageView)
imageView.kf.setImage(with: url)
}
}
但是当我点击播放按钮时,出现以下错误:
Warning: Attempt to present <AVPlayerViewController: 0x7fe8b200b000> on <SweatNet.MainTabBarController: 0x7fe8b4816400> whose view is not in the window hierarchy!
它出现在这一行:self.window?.rootViewController?.present。我认为我调用 rootViewController(似乎是 SweatNet.MainTabBarController)让我感到困惑。我想让它调用 SweatNet.TagViewController。这是包含自定义 MediaPlayerView 的单元格的那个,但我不明白如何获得对此的引用。
试试这个:
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
将你的方法写成:
@objc func tapDetected() {
print("tap!")
let player = AVPlayer(url: self.mediaURL!)
let controller = AVPlayerViewController()
controller.player = player
topViewController().present(controller, animated: true) {
player.play()
}
}
这将使您 return 在层次结构中排在首位 vc。希望对您有所帮助!
我对有关此答案的一些反馈很感兴趣,因为我认为这是我以错误的方式思考 iOS 系统的一个例子。我最终意识到我试图将视图控制器代码放入自定义视图中。查看控制器代码是启动 AVPlayerViewController 的代码。相反,我应该将此逻辑放在我的视图控制器中。
我所做的工作是在视图控制器内部使用 collectionView 委托方法 didSelectItemAt indexPath: IndexPath,其中包含集合视图的视图控制器逻辑,如下所示:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.collectionView {
let post = posts[indexPath.row]
if post.isVideo == true {
self.performSegue(withIdentifier: "playVideo", sender: nil)
} else {
print("is image. might go full screen one day here")
}
}
这有效,我相信它比编写视图的自定义方面来启动 AVPlayerViewController 更好。但也可能这两种方法都有效——我真的对 iOS 开发了解不够,无法做出这样的决定。欢迎任何意见。