前言
應項目需求, 實現視頻播放并支持從列表到全屏到詳情的流程過渡, 并在過渡中保持視頻播放狀態, 同時橫屏視頻模式下, 全屏或詳情頁面時旋轉可以自動切換, 返回時順序跳轉.
Demo中完美還原以上需求, 實現方式具有一定參考價值, 但并不一定適用于其他需求, 僅供參考.
預覽
概述
通過UIViewController
的present/dismiss
進行從視頻列表到全屏或者詳情頁的跳轉.
通過自定義present/dismiss
動畫實現過渡效果.
總共分為4個頁面:
- 視頻列表頁面
- 視頻豎屏全屏頁面
- 視頻橫屏全屏頁面
- 視頻詳情頁面 (帶評論列表的那種)
跳轉分為兩種流程:
- 列表 -> 全屏 -> 詳情
- 列表 -> 詳情 -> 全屏
動畫效果均為從原有頁面視頻位置過渡到目標頁面視頻位置.
播放器使用的是AVPlayer
, 通過AVPlayerLayer
展示視頻畫面, 全局只有一個播放器和一個對應的AVPlayerLayer
, 過渡中通過切換AVPlayerLayer
的所屬視圖完成效果.
支持AVPlayerLayer
的AVLayerVideoGravity
屬性切換不同模式的動畫效果.
對過渡處理做了簡單封裝, 以保證未來需求變動增加新頁面跳轉時可以快速適配.
問題記錄
Q: 橫豎屏切換 原有頁面顯示異常
A:
在原有頁面的viewWillAppear(_:)
中重新設置view
的frame
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 解決橫豎屏切換時 view異常
view.frame = UIScreen.main.bounds
}
Q: 動畫中 AVPlayerLayer
無法和View
的效果同步
A:
重寫UIView
的layoutSubviews()
方法 獲取bounds.size
的動畫對象, 通過CATransaction
重新設置Layer
的frame
屬性與View
的bounds
同步.
override func layoutSubviews() {
super.layoutSubviews()
if let animation = layer.animation(forKey: "bounds.size") {
CATransaction.begin()
CATransaction.setAnimationDuration(animation.duration)
CATransaction.setAnimationTimingFunction(animation.timingFunction)
layer.sublayers?.forEach({ $0.frame = bounds })
CATransaction.commit()
} else {
layer.sublayers?.forEach({ $0.frame = bounds })
}
}
Q: 切換AVPlayerLayer
的videoGravity
屬性時 如何與過渡動畫一致?
A:
在過渡的動畫塊中使用CATransaction
為videoGravity
賦值.
CATransaction.begin()
CATransaction.setAnimationDuration(duration)
CATransaction.setAnimationTimingFunction(.easeInOut)
playerLayer.videoGravity = self.targetGravity
CATransaction.commit()
Q: 自定義present
跳轉橫屏頁面時為何返回后原頁面也變成了橫屏
A:
目標頁面的modalPresentationStyle
屬性要使用.fullScreen
, 不要使用.custom
controller.modalPresentationStyle = .fullScreen
Q: 跳轉橫屏頁面時 原有頁面出現布局效果異常的情況
A:
如果使用AutoLayout
布局, 跳轉橫屏頁面時 SafeArea
會發生變化, 這會影響到原豎屏頁面的布局, 所以這里視頻頁面布局時不建議使用SafeArea
, Demo中我使用了Inch工具來解決適配細節.
總結
視頻跳轉全屏的實現千千萬, 但每種方案都有利有弊, 舉個例子.
常見的UIView
直接添加到當前UIWindow
上然后做視圖動畫來達到全屏效果, 優點: 方便快捷 實現簡單, 缺點: statusBar
以及全面屏設備的底部橫線"Home
"還處于豎屏狀態 并且還要同步處理系統音量視圖的旋轉等等, 改動范圍比較雜, 有些得不償失的感覺.
還有一種是在原頁面中操作視頻視圖做過渡動畫, 動畫結束后再通過無動畫的present
切換到目標頁面, 這種方法較前者的優點在于利用了UIViewController
的一些特性, 同時也解決了statusBar
方向等問題, 但是缺點也是有的, 對應原頁面的依賴和影響過于強烈, 因為要在原頁面中處理動畫等.
最后說說看為何我會選擇自定義present
動畫的這種方案, 首先 要說一句真理: 在Apple的平臺上搞事情 最好還是按照Apple推薦的方式來做比較穩妥, 不然坑并不會比Apple挖的少, 可以玩騷操作, 但是要能承擔得起所帶來的問題, 充分利用UIViewController
的特性并且將處理代碼進行封裝, 方便后續適配新頁面跳轉, 不牽扯其他地方的改動, 僅僅是頁面跳轉而已, 維護成本相對較低.