SwiftUI中@State属性currIndex更新时didSet不触发如何联动更新属性
原因说明
SwiftUI 中@State属于属性包装器类型,你在属性声明位置编写的willSet/didSet观察器,仅在直接对该属性本身赋值时才会触发。
当你通过$currIndex获取绑定(Binding)传入轮播组件后,组件对索引值的修改是直接操作@State包装器的内部底层存储,不会走属性本身的setter调用路径,因此你写的两个属性观察器完全不会执行,控制台也不会有对应打印。
解决方案
优先使用SwiftUI原生提供的状态监听能力实现同步逻辑,两种常用实现方式如下:
方案1:使用.onChange修饰符监听状态变化(推荐)
这是最符合SwiftUI设计范式的写法,不需要修改原有状态定义,直接在视图上挂载监听逻辑即可:
struct ContentView: View { @EnvironmentObject var player: Player // 移除原来写在@State属性上的willSet/didSet @State var currIndex: Int = 0 var body: some View { VStack { ACarousel( player.episodes, id: \.id, index: $currIndex, spacing: width/5, sidesScaling: 0.7, isWrap: true ) { episode in VStack { Image(episode.image) } } } // 监听currIndex变化,自动同步当前播放节目 .onChange(of: currIndex) { newIndex in player.currentEpisode = player.episodes[newIndex] } // 视图首次加载时初始化一次当前节目,避免初始状态不匹配 .onAppear { player.currentEpisode = player.episodes[currIndex] } } }
适配提示:如果你的项目最低部署目标为iOS 17/macOS 14及以上,可以使用带新旧值参数的
onChange版本:.onChange(of: currIndex) { _, newValue in player.currentEpisode = player.episodes[newValue] }
方案2:自定义Binding实现set逻辑注入
如果需要更灵活的控制,也可以手动构造绑定实例传入组件,在绑定的set方法中插入同步逻辑:
struct ContentView: View { @EnvironmentObject var player: Player @State private var currIndex: Int = 0 // 自定义传入组件的绑定 private var carouselIndexBinding: Binding<Int> { Binding( get: { currIndex }, set: { newIndex in currIndex = newIndex // 值更新时同步当前播放节目 player.currentEpisode = player.episodes[newIndex] } ) } var body: some View { VStack { // 传入自定义绑定,不直接使用$currIndex ACarousel( player.episodes, id: \.id, index: carouselIndexBinding, spacing: width/5, sidesScaling: 0.7, isWrap: true ) { episode in VStack { Image(episode.image) } } } .onAppear { player.currentEpisode = player.episodes[currIndex] } } }
内容的提问来源于stack exchange,提问作者user3574603
相关产品推荐
相关产品推荐

