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

如何防止列表项移除时NavigationLink自动返回

解决方案

首先明确问题根源:当device从devices数组移除时,绑定它的ForEach元素会被销毁,对应的NavigationLink也随之消失,导致导航自动返回。我们可以通过在进入详情页时暂停列表数据源的更新通知、离开时恢复的方式,阻止列表因数据源变化重新渲染,从而保留对应的NavigationLink。

1. 定义基础暂停型ObservableObject

先创建提供暂停/恢复更新能力的基础类:

import Combine

class PausableObservableObject: ObservableObject {
    private var isPaused = false
    
    // 暂停更新通知
    func pause() {
        isPaused = true
    }
    
    // 恢复更新并同步最新状态
    func resume() {
        isPaused = false
        objectWillChange.send()
    }
    
    // 仅在非暂停状态下发更新通知
    func publishWillUpdate() {
        if !isPaused {
            objectWillChange.send()
        }
    }
}

2. 实现泛型封装类Pausable

创建泛型类封装任意类型的数据源,监听原数据源变化并在非暂停状态下同步更新:

class Pausable<T>: PausableObservableObject {
    @Published var value: T
    
    private var cancellable: AnyCancellable?
    
    init(initialValue: T, publisher: some Publisher<T, Never>) {
        self.value = initialValue
        super.init()
        
        // 订阅原数据源的变化
        cancellable = publisher
            .sink { [weak self] newValue in
                guard let self = self else { return }
                self.value = newValue
                self.publishWillUpdate()
            }
    }
}

3. 修改视图层逻辑

调整ContentView,用Pausable封装数据源

struct ContentView: View {
    @StateObject private var datasource = Datasource()
    @StateObject private var pausableDevices: Pausable<[Device]>
    
    init() {
        let initialDevices = datasource.devices
        _pausableDevices = StateObject(wrappedValue: Pausable(initialValue: initialDevices, publisher: datasource.$devices))
    }
    
    var body: some View {
        NavigationView {
            DeviceList(pausableDevices: pausableDevices)
                .padding()
                .navigationTitle("Demo")
                .navigationBarTitleDisplayMode(.inline)
        }
        .navigationViewStyle(.stack)
    }
}

修改DeviceList,控制暂停/恢复时机

struct DeviceList: View{
    let pausableDevices: Pausable<[Device]>
    
    var body: some View{
        if pausableDevices.value.isEmpty {
            Text("Scanning...")
        } else {
            ScrollView{
                LazyVStack {
                    ForEach(pausableDevices.value) { device in
                        NavigationLink{
                            DetailView(device: device)
                                // 进入详情页暂停更新
                                .onAppear {
                                    pausableDevices.pause()
                                }
                                // 离开详情页恢复更新
                                .onDisappear {
                                    pausableDevices.resume()
                                }
                        } label: {
                            DeviceItem(device: device)
                        }
                        .buttonStyle(.plain)
                    }
                }
            }
        }
    }
}

关键说明

  • 进入DetailView时调用pause(),此时原datasource.devices变化不会触发列表重新渲染,对应的NavigationLink会被保留。
  • 离开DetailView时调用resume(),恢复更新并同步最新设备列表。
  • 确保Device类型遵循Identifiable协议,否则ForEach无法正确识别元素。

内容的提问来源于stack exchange,提问作者Ricky Mo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:14:58