SwiftUI自定义View无相关变更却重绘body的原因咨询
嘿,我来帮你拆解下这个问题~ 你遇到的情况在SwiftUI开发里挺常见的,咱们一步步捋清楚:
首先得回忆下SwiftUI的重绘逻辑:当一个View依赖的状态(比如@State、@Published属性)发生变化时,它的body会被重新计算;同时父View的body重绘时,会重新创建所有子View的实例(因为View都是值类型的struct)。但正常来说,如果子View的输入参数没有变化,SwiftUI会跳过它的body渲染——前提是这个View遵循了Equatable协议。
回到你的代码里,第二个DemoDayView绑定的是viewModel.monthDate,你明明只修改了dayDate,它却跟着重绘,大概率是以下原因:
你的
DemoDayView没有遵循Equatable协议:SwiftUI默认无法判断两个DemoDayView实例是否等价,哪怕传入的date完全一样,当父View重绘时,它会默认认为这个子View的状态变了,从而触发body重绘。解决办法很简单,让DemoDayView遵循Equatable:struct DemoDayView: View, Equatable { static func == (lhs: DemoDayView, rhs: DemoDayView) -> Bool { // 这里比较所有影响View渲染的参数,比如date lhs.date == rhs.date } let date: Date // 其他View代码... }要是你的View参数都是Equatable类型,甚至可以直接用自动合成的实现,不用自己写
==方法:struct DemoDayView: View, Equatable { let date: Date // 其他View代码... }另一种小概率可能:
viewModel.monthDate被间接修改了:虽然你在tap手势里只改了dayDate,但可以检查下viewModel里有没有其他逻辑,比如修改dayDate时不小心连带更新了monthDate?或者monthDate是计算属性,它的依赖值悄悄变了?不过这种情况相对少见,先优先检查Equatable的问题。
另外补充个小技巧:如果你不想让某个子View跟着父View重绘,除了遵循Equatable,还可以给View加.equatable()修饰符,或者设置一个稳定的id,不过遵循Equatable是最规范的做法。
备注:内容来源于stack exchange,提问作者kunass2

