SwiftUI强制子视图重绘:设备旋转后视图未更新问题求助
解决SwiftUI设备旋转时视图不重绘、GeometryReader数据未更新的问题
让我来帮你梳理下代码里的问题,再一步步给出修复方案:
问题根源分析
- 自定义
objectWillChange冲突:你在ViewModel里手动声明了@Published var objectWillChange = ObservableObjectPublisher(),但ObservableObject协议已经默认提供了这个发布者,自定义会覆盖默认实现,导致状态变更无法正确通知视图。 - 无绑定的状态触发:即使你调用了
objectWillChange.send(),但视图没有依赖ViewModel的任何@Published属性,SwiftUI无法感知到需要重绘。 onAppear仅触发一次:View2里的GeometryReader只在视图首次加载时执行onAppear,设备旋转时不会重新计算尺寸。
修复方案
1. 修正ViewModel逻辑
去掉自定义的objectWillChange,用一个@Published属性来触发视图更新:
extension StackView { final class ViewModel: ObservableObject { // 用这个属性触发视图重绘,旋转时切换状态 @Published var refresh = false init() { NotificationCenter.default.addObserver( self, selector: #selector(updateView(_:)), name: UIDevice.orientationDidChangeNotification, object: nil ) } deinit { NotificationCenter.default.removeObserver( self, name: UIDevice.orientationDidChangeNotification, object: nil ) } @objc func updateView(_ notification: Notification) { // 切换refresh状态,自动触发objectWillChange通知 refresh.toggle() } } }
2. 修改StackView,让子视图依赖状态变更
通过id绑定refresh属性,当状态变化时强制重绘View2:
struct StackView: View { @ObservedObject var vm = ViewModel() var body: some View { Color.white .overlay( VStack { Spacer() VStack { Text("Hello StackView") // 用refresh状态作为id,状态变化时重新创建View2 View2() .id(vm.refresh) } } ) .onAppear { // 确保开启设备旋转通知监听 UIDevice.current.beginGeneratingDeviceOrientationNotifications() } .onDisappear { UIDevice.current.endGeneratingDeviceOrientationNotifications() } } }
3. 优化View2,实时监听尺寸变化
把onAppear改成onChange,确保旋转时能获取最新的Geometry数据:
struct View2: View { var body: some View { Text("Hello view 2") .overlay(GeometryReader { geo in Color.clear .onAppear { print("初始尺寸:", geo.frame(in: .global)) } // 监听尺寸变化,旋转时实时获取新数据 .onChange(of: geo.frame(in: .global)) { newFrame in print("更新后尺寸:", newFrame) } }) } }
额外简化方案(无需ViewModel)
如果不需要ViewModel做额外逻辑,也可以直接在视图中监听通知:
struct StackView: View { @State private var refresh = false var body: some View { Color.white .overlay( VStack { Spacer() VStack { Text("Hello StackView") View2() .id(refresh) } } ) .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in refresh.toggle() } .onAppear { UIDevice.current.beginGeneratingDeviceOrientationNotifications() } .onDisappear { UIDevice.current.endGeneratingDeviceOrientationNotifications() } } }
内容的提问来源于stack exchange,提问作者Jorgen
相关产品推荐
相关产品推荐

