以下是一篇关于在 iOS 中实现画中画(PiP)功能的技术博客:
iOS 画中画(PiP)功能实现指南
简介
画中画(Picture in Picture, PiP)是一项允许用户在使用其他应用时继续观看视频内容的功能。本文将详细介绍如何在 iOS 应用中实现 PiP 功能。
系统要求
- iOS 15.0 及以上版本
- AVKit 框架
核心组件
实现 PiP 功能主要涉及以下几个核心组件:
AVPictureInPictureController
- 负责管理 PiP 会话AVPictureInPictureControllerContentSource
- 定义 PiP 内容源AVPictureInPictureVideoCallViewController
- 控制 PiP 窗口的视图控制器
实现步骤
1. 检查设备支持
首先需要检查设备是否支持 PiP 功能:
- (BOOL)isSupported {if (@available(iOS 15.0, *)) {return [AVPictureInPictureController isPictureInPictureSupported];}return NO;
}
2. 创建 PiP 视图
需要创建一个自定义视图来显示 PiP 内容:
@interface PipView : UIView
@end@implementation PipView
+ (Class)layerClass {return [AVSampleBufferDisplayLayer class];
}
@end
3. 配置 PiP 控制器
设置 PiP 控制器需要以下步骤:
- (BOOL)setup:(PipOptions *)options {if (!self.isSupported) {return NO;}if (@available(iOS 15.0, *)) {// 创建 PiP 视图_pipView = [[PipView alloc] init];// 创建视图控制器AVPictureInPictureVideoCallViewController *pipViewController = [[AVPictureInPictureVideoCallViewController alloc] init];pipViewController.preferredContentSize = options.preferredContentSize;[pipViewController.view addSubview:_pipView];// 创建内容源AVPictureInPictureControllerContentSource *contentSource =[[AVPictureInPictureControllerContentSource alloc]initWithActiveVideoCallSourceView:options.sourceContentViewcontentViewController:pipViewController];// 初始化 PiP 控制器_pipController = [[AVPictureInPictureController alloc]initWithContentSource:contentSource];_pipController.delegate = self;_pipController.canStartPictureInPictureAutomaticallyFromInline = options.autoEnterEnabled;return YES;}return NO;
}
4. 控制 PiP 会话
启动 PiP:
- (BOOL)start {if (!self.isSupported) {return NO;}dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 0.1),dispatch_get_main_queue(), ^{if (self->_pipController.isPictureInPicturePossible) {[self->_pipController startPictureInPicture];}});return YES;
}
停止 PiP:
- (void)stop {if (self->_pipController.isPictureInPictureActive) {[self->_pipController stopPictureInPicture];}
}
释放资源:
- (void)dispose {if (@available(iOS 15.0, *)) {self->_pipController.contentSource = nil;}if (self->_isPipActived) {self->_isPipActived = NO;[self->_pipStateDelegate pipStateChanged:PipStateStopped error:nil];}
}
5. 实现状态回调
通过实现 AVPictureInPictureControllerDelegate
协议来处理 PiP 状态变化:
- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {_isPipActived = YES;[_pipStateDelegate pipStateChanged:PipStateStarted error:nil];
}- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {_isPipActived = NO;[_pipStateDelegate pipStateChanged:PipStateStopped error:nil];
}- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureControllerfailedToStartPictureInPictureWithError:(NSError *)error {[_pipStateDelegate pipStateChanged:PipStateFailed error:error.description];
}
注意事项
- PiP 功能仅支持 iOS 15.0 及以上版本
- 启动 PiP 时需要适当延迟以确保正常显示
- 自动进入 PiP 模式需要在 setup 时配置
autoEnterEnabled
选项 - 释放资源时建议使用
contentSource = nil
而不是直接调用stopPictureInPicture
- PiP 窗口的默认大小建议设置为至少 100x100,否则可能导致启动失败
最佳实践
- 在初始化时检查设备是否支持 PiP 功能
- 实现适当的错误处理和状态回调
- 在应用进入后台时,如果启用了自动进入选项,PiP 会自动启动
- 注意内存管理,及时释放不需要的资源
总结
iOS 的 PiP 功能实现主要依赖于 AVKit 框架,通过合理配置 AVPictureInPictureController
及其相关组件,可以为用户提供流畅的画中画体验。在实现过程中需要注意版本兼容性、状态管理和资源释放等问题。
参考
- Adopting Picture in Picture for video calls
PS
本次实现还不太全面,出来的PIP窗口只是一个背景黑色,还没有实现视频的显示,后续会持续更新。