You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI强制子视图重绘:设备旋转后视图未更新问题求助

解决SwiftUI设备旋转时视图不重绘、GeometryReader数据未更新的问题

让我来帮你梳理下代码里的问题,再一步步给出修复方案:

问题根源分析

  1. 自定义objectWillChange冲突:你在ViewModel里手动声明了@Published var objectWillChange = ObservableObjectPublisher(),但ObservableObject协议已经默认提供了这个发布者,自定义会覆盖默认实现,导致状态变更无法正确通知视图。
  2. 无绑定的状态触发:即使你调用了objectWillChange.send(),但视图没有依赖ViewModel的任何@Published属性,SwiftUI无法感知到需要重绘。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:45:57