iOS10中AVPlayerViewController导致弹窗内底层模态视图控制器异常
解决iOS 10中AVPlayerViewController横屏后底层模态弹窗异常的问题
嘿,这个iOS 10的模态旋转bug我之前踩过坑!结合你的场景——竖屏应用里,层级是TabBar控制器(根)→A→弹窗B→AVPlayerViewController(AVP),当弹出AVP后旋转设备到横屏,底层的B弹窗出现异常,这其实是iOS 10特有的旋转事件传递和状态同步bug:当嵌套的模态控制器中存在支持横屏的实例时,底层固定竖屏的控制器会丢失正确的布局状态,导致界面错乱。
下面给你几个可行的解决方案,按推荐程度排序:
方案一:自定义AVPlayerViewController子类(最稳妥)
通过自定义AVP的旋转行为,确保它在消失时强制回到竖屏,避免影响底层控制器:
- 创建
CustomAVPlayerViewController子类:
import AVKit class CustomAVPlayerViewController: AVPlayerViewController { // 允许AVP自动旋转 override var shouldAutorotate: Bool { return true } // 按需设置支持的方向,这里选除了倒竖屏的所有方向 override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .allButUpsideDown } // 当AVP即将消失时,强制将设备转回竖屏 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) guard let window = UIApplication.shared.keyWindow else { return } UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation") } }
- 在弹窗B中使用这个子类来弹出播放控制器,替换原生的
AVPlayerViewController即可。
方案二:给弹窗B添加旋转监听与修复
如果不想自定义AVP子类,可以在弹窗B中监听旋转事件,在AVP活跃时强制修复布局:
- 在B的
viewDidLoad中注册旋转通知:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver( self, selector: #selector(handleRotation), name: UIDevice.orientationDidChangeNotification, object: nil ) }
- 实现布局修复逻辑:
@objc private func handleRotation() { // 只有当AVP已经弹出时才处理 guard presentedViewController is AVPlayerViewController else { return } // 延迟一点时间,等旋转动画完成后再修复布局 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.view.setNeedsLayout() self.view.layoutIfNeeded() } } // 记得在销毁时移除通知 deinit { NotificationCenter.default.removeObserver(self) }
方案三:全局控制旋转权限
通过根TabBarController的旋转代理方法,确保只有当前最上层的控制器能控制旋转方向,避免底层控制器被干扰:
- 自定义根TabBarController:
class CustomTabBarController: UITabBarController { override var shouldAutorotate: Bool { return selectedViewController?.shouldAutorotate ?? false } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return selectedViewController?.supportedInterfaceOrientations ?? .portrait } }
- 在弹窗B中固定旋转设置:
class BViewController: UIViewController { override var shouldAutorotate: Bool { return false } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } }
这样设置后,只有AVP能响应旋转,底层的B和TabBar都会保持竖屏状态,从根源避免状态混乱。
额外说明
这个bug在iOS 11及以上版本已经被苹果官方修复了,如果你的应用最低支持版本可以升级到iOS 11,这个问题会自动消失。但如果必须兼容iOS 10,上面的方案都能有效解决底层弹窗异常的问题。
内容的提问来源于stack exchange,提问作者iur
相关产品推荐
相关产品推荐

