SwiftUI中.onGeometryChange与Padding引发无限更新循环问题
这个问题完全和SwiftUI的布局评估逻辑以及修饰符的作用顺序有关,具体拆解:
为什么padding在onGeometryChange前面会无限循环?
当你把.padding()放在.onGeometryChange之前时,视图的层级结构是:VStack → frame(maxWidth: .infinity) → padding → onGeometryChange
此时.onGeometryChange监听的是添加padding之后的整个视图的尺寸。当你在action回调里修改textTest.width,这个值会直接影响内部Text的宽度——Text变宽后,外层的VStack尺寸也会随之变化,进而导致padding后的整体尺寸改变,触发.onGeometryChange再次执行回调,再次修改width,形成无限循环。而且因为padding是基于当前视图尺寸计算的,每次循环都会让尺寸被放大,所以你会看到width变成几千的异常值。
为什么padding在onGeometryChange后面就正常?
把.padding()移到.onGeometryChange之后时,视图层级变成:VStack → frame(maxWidth: .infinity) → onGeometryChange → padding
这时.onGeometryChange监听的是VStack应用了frame(maxWidth: .infinity)后的尺寸——这个尺寸是父容器的可用宽度(比如屏幕宽度),是固定不变的。后面加的padding只是给这个固定尺寸的视图再套一层内边距,但不会改变被监听的那个视图的尺寸。所以修改textTest.width只会改变内部Text的宽度,不会影响被监听的视图尺寸,自然不会触发.onGeometryChange的重复回调,也就不会有循环。
核心逻辑总结
SwiftUI的修饰符是按顺序逐层包裹视图的,每个修饰符都会生成一个新的视图层级。.onGeometryChange监听的是它直接作用的那个视图的尺寸变化,所以顺序直接决定了监听的是哪一层的尺寸。如果被监听的视图尺寸会被回调里的状态修改所影响,就会形成循环;反之则不会。
内容的提问来源于stack exchange,提问作者reza23

