Swift UI 如何解决设备旋转时状态变量不自动更新的问题
核心问题定位
- 原代码中直接读取的
UIDevice.current.orientation不属于SwiftUI响应式状态体系,设备旋转时系统不会触发基于该值的视图重计算 - 设备旋转时虽然通过
@Environment读取的sizeClass值会正常更新,但和方向绑定的布尔计算属性因为没有打通可观察的旋转状态通道,会一直停留在首次加载的计算结果
修改方案
1. 实现支持响应式的设备方向管理器
用ObservableObject封装设备方向状态,监听系统旋转通知自动更新状态值,打通到SwiftUI的状态体系:
import SwiftUI import UIKit final class DeviceOrientationManager: ObservableObject { // 状态变更时自动通知所有观察的视图刷新 @Published var currentOrientation: UIDeviceOrientation static let shared = DeviceOrientationManager() private init() { currentOrientation = UIDevice.current.orientation // 注册系统旋转通知监听 NotificationCenter.default.addObserver( self, selector: #selector(orientationDidChange), name: UIDevice.orientationDidChangeNotification, object: nil ) } @objc private func orientationDidChange() { let newOrientation = UIDevice.current.orientation // 过滤无效方向(未知/平放),避免旋转瞬间的判断闪烁 guard newOrientation.isPortrait || newOrientation.isLandscape else { return } currentOrientation = newOrientation } }
2. 适配原有SizeClassAdjustable协议
不改动原有计算属性的业务逻辑,仅升级协议要求,让遵循协议的组件可以接入方向管理器的可观察状态:
protocol SizeClassAdjustable { var verticalSizeClass: UserInterfaceSizeClass? { get } var horizontalSizeClass: UserInterfaceSizeClass? { get } // 新增协议要求:接入全局方向管理对象 var orientationManager: DeviceOrientationManager { get } } extension SizeClassAdjustable { // 完全复用原有计算属性逻辑,仅替换方向值的读取源 var isPad: Bool { horizontalSizeClass == .regular && verticalSizeClass == .regular } var isPadLandscape: Bool { isPad && orientationManager.currentOrientation.isLandscape } var isPadPortrait: Bool { isPad && orientationManager.currentOrientation.isPortrait } var isPadOrLandscapeMax: Bool { horizontalSizeClass == .regular } var isLandscapeMax: Bool { horizontalSizeClass == .regular && verticalSizeClass == .compact } var isLandscape: Bool { verticalSizeClass == .compact } var isPortrait: Bool { verticalSizeClass == .regular } }
3. 视图层接入状态
在原有的ContentView中注入可观察的方向管理器,确保状态变化时自动触发视图重渲染,原有视图判断逻辑完全不需要改动:
struct ContentView3: View, SizeClassAdjustable { // 观察方向管理器的状态变更 @StateObject private var orientationManager = DeviceOrientationManager.shared @Environment(\.verticalSizeClass) var _verticalSizeClass @Environment(\.horizontalSizeClass) var _horizontalSizeClass // 满足协议要求的属性映射 var verticalSizeClass: UserInterfaceSizeClass? { _verticalSizeClass } var horizontalSizeClass: UserInterfaceSizeClass? { _horizontalSizeClass } var body: some View { if(self.isPadLandscape){ Text("iPad Landscape") }else if(self.isPadPortrait){ Text("iPad Portrait") }else if(self.isPortrait){ Text("iPhone Portrait") }else if(self.isLandscape){ Text("iPhone Landscape") } } }
内容的提问来源于stack exchange,提问作者benpomeroy9
相关产品推荐
相关产品推荐

