AVPlayer 全屏切換時,直接修改 AVPlayerLayer.frame 或 videoGravity 會被播放器內部重置,導致畫面彈回原尺寸。正確做法是調整 承載 PlayerLayer 的 UIView 尺寸,PlayerLayer 會自動適配父視圖 bounds。
// 錯誤:直接改 Layer frame,播放器內部會重置
playerLayer.frame = CGRectMake(0, 0, screenWidth, screenHeight);
// 1. 結構:ContainerView (自動布局) → PlayerView (承載 PlayerLayer) → AVPlayerLayer
@property (nonatomic, strong) UIView *playerView; // 專門承載 layer 的 view
@property (nonatomic, strong) AVPlayerLayer *playerLayer;
// 初始化
self.playerView = [[UIView alloc] init];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; // 或 ResizeAspectFill
[self.playerView.layer addSublayer:self.playerLayer];
// 關鍵:在 playerView layoutSubviews 中同步 layer frame
- (void)layoutSubviews {
[super layoutSubviews];
self.playerLayer.frame = self.playerView.bounds;
}
// 2. 全屏切換:只需調整 playerView 的 frame/constraints
- (void)toggleFullscreen:(BOOL)fullscreen {
if (fullscreen) {
// 移除原約束,添加全屏約束(或 frame = screenBounds)
[self.view addSubview:self.playerView];
self.playerView.translatesAutoresizingMaskIntoConstraints = YES;
self.playerView.frame = [UIScreen mainScreen].bounds;
} else {
// 恢復原約束/frame
[self.contentView addSubview:self.playerView];
// 恢復 Auto Layout 約束...
}
// playerView.bounds 變化 → layoutSubviews → playerLayer.frame 自動同步
}
AVPlayerLayer 只負責渲染,UIView 負責佈局與動畫。playerView 尺寸/位置,旋轉、分屏、多任務自動適配。AVLayerVideoGravityResizeAspect 保持比例縮放,ResizeAspectFill 裁剪填滿。layoutSubviews 中同步 bounds,否則播放器內部定時器會重置。此模式適用於所有基於 CALayer 的渲染組件(如 GLKView、MTKView、WKWebView 內容層),是 iOS 視圖層級動畫佈局的核心範式。