SwiftUI中@State移除数组最后一项时不触发onChange及绑定相关问题求助
SwiftUI中@State移除数组最后一项时不触发onChange及绑定相关问题求助
大家好,我最近在SwiftUI开发里遇到了两个挺奇怪的问题,折腾了好一阵都没搞明白,希望能得到各位大佬的帮助🙏
问题1:移除数组最后一项变为空数组时,onChange(of: sounds)不触发
我在BSTabView里用@State维护了一个selectedSounds数组,通过@Binding把它传给下层的BSSoundPlayerView。现在发现一个诡异的情况:
- 当调用数组的
.append()或者移除非最后一项的.remove()时,BSSoundPlayerView里的onChange(of: sounds, initial: true)都会正常触发,控制台能打印出对应的日志 - 但当我移除数组里的最后一项,让数组变成空数组
[]的时候,这个onChange回调完全没反应
我怀疑这个问题和我新增的@Binding层有关——之前BSSoundPlayerView是嵌套在内部视图里的,不需要和BSTabView做跨视图状态绑定,后来因为要让播放器在所有标签页都能显示,才把它移到了BSTabView的层级里,用@Binding来同步状态。
问题2:在sounds的onChange里设置isPlaying = true,不会触发isPlaying的onChange
另外还有个问题:如果我在onChange(of: sounds, initial: true)这个回调里设置isPlaying = true,那么BSSoundPlayerView里的onChange(of: isPlaying)完全不会被触发,控制台也看不到对应的打印日志,这也让我摸不着头脑。
下面是相关的代码片段(为了可读性做了精简,用...省略了无关部分):
struct BSTabView: View { @State private var selectedSounds: [BSComposerViewSoundType] = [] @State private var isPlaying: Bool = false @State private var showAlert: Bool = false @State private var tabSelectionIndex = 1 var body: some View { ZStack(alignment: .bottom) { TabView(selection: $tabSelectionIndex) { Group { ... BSBackgroundView { BSComposerView(selectedSounds: $selectedSounds, callback: selectedSound) } .tabItem { Label("Composer", systemImage: "music.quarternote.3") } .tag(1) ... } } if selectedSounds.count > 0 { BSSoundPlayerView(sounds: $selectedSounds, isPlaying: $isPlaying) .offset(y: -60) } } } // BSComposerView callback func selectedSound(_ sound: BSComposerViewSoundType) { if let index = selectedSounds.firstIndex(of: sound) { self.selectedSounds.remove(at: index) } else if selectedSounds.count < 3 { self.selectedSounds.append(sound) } else if selectedSounds.count >= 3 { showAlert.toggle() } } } struct BSSoundPlayerView: View { @Binding var sounds: [BSComposerViewSoundType] @Binding var isPlaying: Bool @State private var title: String = "Current Mix" @State private var showPlayerDetailSheet = false var body: some View { HStack(alignment: .center) { ... } .onChange(of: sounds, initial: true) { oldValue, newValue in print("onChange(of: sounds, initial: true)") guard !sounds.isEmpty else { clear() return } let unselectedSounds = oldValue.filter { !newValue.contains($0) } for unselectedSound in unselectedSounds { clear(soundFileName: unselectedSound.soundFileName) } guard !newValue.isEmpty else { return pause() } play() } .onChange(of: isPlaying) { print("onChange(of: isPlaying)") if isPlaying == true { play() } else { pause() } } } // 省略clear、play、pause等方法 ... }
备注:内容来源于stack exchange,提问作者Ace Green
相关产品推荐
相关产品推荐

