iOS Swift实现仿Teams悬浮通话窗口:支持最大化与最小化
iOS 实现全局悬浮通话窗口(类似Microsoft Teams)
可以实现这种全局悬浮的通话窗口功能,iOS平台有两种主流技术方案,分别对应不同的自定义需求:
方案一:系统原生画中画(Picture in Picture)框架(官方推荐)
这是iOS为视频通话/媒体播放场景提供的原生悬浮方案,天然支持在主屏幕和其他应用上方显示,步骤如下:
- 配置权限:
- 在
Info.plist中添加UIBackgroundModes并勾选voip,确保应用后台通话时保持活跃 - 添加
NSPictureInPictureUsageDescription,说明画中画功能的用途
- 在
- 集成AVKit框架实现PiP:
将你的视频通话视图包装为AVPlayerLayer,通过AVPictureInPictureController启动悬浮模式:import AVKit // 初始化PiP控制器(yourVideoPlayerLayer为你的视频画面层) guard let pipController = try? AVPictureInPictureController(playerLayer: yourVideoPlayerLayer) else { return } pipController.delegate = self // 用户点击最小化时启动PiP if pipController.isPictureInPictureSupported { pipController.startPictureInPicture() } - 自定义交互:通过实现
AVPictureInPictureControllerDelegate协议,处理PiP状态切换事件,比如从悬浮窗切回应用全屏。
优点:系统原生支持,无需复杂的后台状态管理,兼容性好;缺点:悬浮窗样式受系统限制,自定义程度较低。
方案二:自定义全局悬浮窗口(基于UIWindow+VOIP后台权限)
如果需要完全自定义悬浮窗的样式、大小和拖拽交互(和Teams的效果完全匹配),可以采用这种方案:
- 配置后台权限:
- 开启VOIP后台模式(同方案一),同时集成
PushKit接收VOIP推送,确保应用在后台不会被系统杀死
- 开启VOIP后台模式(同方案一),同时集成
- 创建自定义悬浮窗口:
class FloatingCallWindow: UIWindow { private let callView = CallCustomView(frame: CGRect(x: 0, y: 0, width: 160, height: 220)) override init(frame: CGRect) { super.init(frame: frame) windowLevel = .alert + 1 // 设置窗口级别高于系统弹窗 backgroundColor = .clear addSubview(callView) // 添加拖拽手势 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) addGestureRecognizer(panGesture) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: self) center = CGPoint(x: center.x + translation.x, y: center.y + translation.y) gesture.setTranslation(.zero, in: self) } } - 在SceneDelegate中管理窗口生命周期:
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? private var floatingWindow: FloatingCallWindow? func sceneDidEnterBackground(_ scene: UIScene) { // 进入后台时显示悬浮窗 guard let windowScene = scene as? UIWindowScene else { return } floatingWindow = FloatingCallWindow(frame: CGRect(x: 40, y: 120, width: 160, height: 220)) floatingWindow?.windowScene = windowScene floatingWindow?.isHidden = false } func sceneWillEnterForeground(_ scene: UIScene) { // 回到前台时隐藏悬浮窗,切换回主通话界面 floatingWindow?.isHidden = true floatingWindow = nil } } - 实现最大化/最小化逻辑:在自定义的
CallCustomView中添加按钮,点击时调整悬浮窗的frame大小,或者切换回主应用的全屏界面。
优点:完全自定义样式和交互,完美匹配Teams的功能;缺点:需要严格维护后台活跃状态,否则应用可能被系统回收,开发成本更高。
关键注意事项
- 必须遵守iOS后台规则:VOIP应用需通过
PushKit接收推送,不能仅依赖后台模式保持活跃 - 自定义悬浮窗的尺寸不能过大,避免被系统判定为违规
- 所有功能需在真机上测试,模拟器对后台窗口和PiP的支持有限
内容的提问来源于stack exchange,提问作者Asad Farooq
相关产品推荐
相关产品推荐

