SwiftUI绘制路径时遇状态更新错误的解决方法咨询
问题解决:修复SwiftUI中"Modifying state during view update"错误
你遇到的错误是因为在GeometryReader内部直接修改@State变量——这会在视图渲染过程中触发状态更新,进而引发新一轮视图刷新,形成循环,导致系统抛出未定义行为的警告。
正确的做法是使用SwiftUI的PreferenceKey机制来安全传递布局尺寸信息,这是官方推荐的在视图间传递布局数据的方式,不会干扰视图更新流程。
修改步骤:
- 定义一个自定义PreferenceKey,用来存储视图的CGRect信息:
struct RectPreferenceKey: PreferenceKey { static var defaultValue: [String: CGRect] = [:] static func reduce(value: inout [String: CGRect], nextValue: () -> [String: CGRect]) { value.merge(nextValue()) { $1 } } }
- 替换原来的GeometryReader逻辑,通过
preference传递rect,而不是直接修改@State:
Text部分的background修改为:
.background { GeometryReader { proxy in Color.clear .preference(key: RectPreferenceKey.self, value: ["title": proxy.frame(in: .named("root"))]) } }
Divider部分同理修改:
.background { GeometryReader { proxy in Color.clear .preference(key: RectPreferenceKey.self, value: ["divider": proxy.frame(in: .named("root"))]) } }
- 在父视图添加
.onPreferenceChange来接收并更新状态:
在ZStack的.coordinateSpace(name: "root")之前添加:
.onPreferenceChange(RectPreferenceKey.self) { preferences in titleRect = preferences["title"] ?? .zero dividerRect = preferences["divider"] ?? .zero }
修改后的完整代码:
struct MyView: View { @State private var titleRect: CGRect = .zero @State private var dividerRect: CGRect = .zero struct RectPreferenceKey: PreferenceKey { static var defaultValue: [String: CGRect] = [:] static func reduce(value: inout [String: CGRect], nextValue: () -> [String: CGRect]) { value.merge(nextValue()) { $1 } } } var body: some View { ZStack { RoundedRectangle(cornerRadius: 25.0) .foregroundColor(.yellow) VStack { Text("Some fancy title …") .border(.green) .background { GeometryReader { proxy in Color.clear .preference(key: RectPreferenceKey.self, value: ["title": proxy.frame(in: .named("root"))]) } } Rectangle() .frame(height: 20.0) .foregroundColor(.clear) Rectangle() .frame(height: 1.0) .background { GeometryReader { proxy in Color.clear .preference(key: RectPreferenceKey.self, value: ["divider": proxy.frame(in: .named("root"))]) } } Spacer() } .padding(.top) Path { path in path.move(to: CGPoint(x: dividerRect.minX, y: titleRect.maxY)) path.addLine(to: CGPoint(x: dividerRect.minX + dividerRect.width * 2/3, y: dividerRect.maxY)) path.addLine(to: CGPoint(x: dividerRect.maxX, y: titleRect.maxY)) } .stroke(.orange, lineWidth: 2) // 给Path添加橙色描边,匹配需求 } .padding() .onPreferenceChange(RectPreferenceKey.self) { preferences in titleRect = preferences["title"] ?? .zero dividerRect = preferences["divider"] ?? .zero } .coordinateSpace(name: "root") } }
额外说明:
- PreferenceKey的
reduce方法用来合并多个视图的偏好值,这里用字典通过key区分不同视图的rect。 - 给Path添加
.stroke(.orange)可以让橙色线条正常显示,符合你的需求。 - 这种方式彻底避免了在视图渲染过程中直接修改状态,解决了"Modifying state during view update"的错误。
内容的提问来源于stack exchange,提问作者appfrosch
相关产品推荐
相关产品推荐

