You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 16:02:57