tvOS竖屏旋转90度后Focus Engine适配问题求助
这个问题我之前帮团队处理过,核心矛盾点很明确:你手动旋转ViewController的视图只是改变了视觉呈现,但系统的Focus Engine依然沿用原始屏幕的横屏坐标系——它根本不知道你偷偷把视图转了90度,所以遥控器手势和你的视觉布局自然就错位了。
给你两个可行的解决方案,从根源解决和适配修复两种思路,按需选择:
方案一:无需旋转视图,直接适配竖屏电视(更推荐)
这个方案从根源上避免了坐标系错位问题,也是我更推荐的做法。核心逻辑是:把竖屏摆放的电视的物理分辨率当成应用的竖屏尺寸来布局,完全不需要旋转任何视图。
具体步骤:
- 调整根视图的尺寸与位置
电视竖屏摆放后,原横屏分辨率(比如1920×1080)会变成竖屏的1080×1920。我们直接让应用的根视图适配这个竖屏尺寸,居中填充到屏幕上:
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: windowScene) self.window = window let screenBounds = UIScreen.main.bounds // 竖屏摆放后的目标尺寸:宽=原屏幕高度,高=原屏幕宽度 let targetSize = CGSize(width: screenBounds.height, height: screenBounds.width) // 设置根视图控制器 let rootVC = YourMainStatusBoardVC() window.rootViewController = rootVC // 调整根视图适配竖屏尺寸 rootVC.view.bounds = CGRect(origin: .zero, size: targetSize) rootVC.view.center = CGPoint(x: screenBounds.midX, y: screenBounds.midY) window.makeKeyAndVisible() } }
所有内容按竖屏尺寸布局
后续你的所有页面、控件(图片、文本、分段控件等)都以竖屏尺寸(比如1080×1920)为基准设置Auto Layout约束。这样内容在竖屏电视上显示完全正常,Focus Engine的坐标系和视觉布局完全对齐,遥控器手势自然就和预期一致了。别忘了适配启动页
调整Launch Screen的布局,让它也适配竖屏电视,避免启动时的显示错位。
方案二:让Focus Engine适配视图旋转(适配修复思路)
如果你因为某些原因必须保留视图旋转的方案,可以通过修改坐标系转换逻辑,让Focus Engine跟着你的旋转视图走。
具体做法是重写视图的坐标转换方法,让Focus Engine在计算焦点位置时,使用旋转后的坐标系:
class PortraitViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let rotationAngle = -CGFloat.pi/2 view.transform = CGAffineTransform(rotationAngle: rotationAngle) // 必须调整视图的bounds和center,避免内容被裁剪 let originalBounds = view.bounds view.bounds = CGRect(x: 0, y: 0, width: originalBounds.height, height: originalBounds.width) view.center = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY) } // 重写坐标转换方法,让Focus Engine使用旋转后的坐标系 override func convert(_ point: CGPoint, to view: UIView?) -> CGPoint { let rotatedPoint = point.applying(self.view.transform.inverted()) return super.convert(rotatedPoint, to: view) } override func convert(_ point: CGPoint, from view: UIView?) -> CGPoint { let convertedPoint = super.convert(point, from: view) return convertedPoint.applying(self.view.transform) } }
另外,对于一些复杂的自定义控件,你可能还需要重写preferredFocusEnvironments或者自定义UIFocusItem的focusFrame属性,确保焦点框的位置和视觉控件对齐。
总结
方案一是最优解,从布局层面直接适配竖屏电视,完全避免了坐标系冲突;方案二更适合临时适配或者无法修改全局布局的场景。
内容的提问来源于stack exchange,提问作者UberJason

