当屏幕位于 NavigationStack 中时保持任务处于活动状态

问题描述 投票:0回答:1

有没有办法在特定屏幕是导航堆栈时保持使用

.task
修改器启动的任务处于活动状态?

例如,在下面的计时器示例中,一旦您导航到另一个计时器,计时器就会停止更新。

struct TimerList: View {
  @State private var times = [0, 1, 2, 3, 4, 5]
  var body: some View {
    NavigationStack {
      List(times, id: \.self) { time in
        NavigationLink(value: time) {
          Text("\(time)")
        }
      }
      .navigationDestination(for: Int.self) { time in
        TimerView(time: time)
      }
    }
  }
}

class TimerViewModel: ObservableObject {
  @Published var time: Int
  
  init(time: Int) {
    self.time = time
  }
  
  @MainActor
  func start() async {
    repeat {
      do { try await Task.sleep(nanoseconds: 1_000_000_000) }
      catch { self.time = 0 }
      self.time += 1
    } while !Task.isCancelled
  }
}

struct TimerView: View {
  let time: Int
  @StateObject var viewModel: TimerViewModel
  
  init(time: Int) {
    self.time = time
    _viewModel = StateObject(wrappedValue: TimerViewModel(time: time))
  }
  
  var body: some View {
    VStack {
      Text("Time is \(viewModel.time)")
      NavigationLink(value: time + 1, label: { Text("Next") })
    }
    .task { await viewModel.start() }
  }
}

有没有办法将任务范围限制在屏幕上,以便它们在导航堆栈中继续运行,但一旦被删除就被取消?

swift swiftui
1个回答
0
投票

尝试下面的代码。

class NavigationManager: ObservableObject {
    @Published var path = NavigationPath()

    func popToRoot() {
        path.removeLast(path.count) // Clears the navigation path
    }

    func push<T: Hashable>(_ item: T) {
        path.append(item) // Adds an item to the navigation path
    }
}

struct TimerList: View {
    @State private var times = [0, 1, 2, 3, 4, 5]

    @StateObject private var navigationManager = NavigationManager()
    @StateObject var viewModel = TimerViewModel()

    var body: some View {
        NavigationStack(path: $navigationManager.path) {
            List(times, id: \.self) { time in
                Button(action: {
                    viewModel.time = time
                    viewModel.start()
                    navigationManager.push(time)
                }) {
                    Text("\(time)")
                }
            }
            .onAppear {
                viewModel.stop()
            }
            .navigationDestination(for: Int.self) { time in
                TimerView()
            }
        }
        .environmentObject(viewModel)
    }
}

class TimerViewModel: ObservableObject {
    @Published var time: Int = 0
    private var task: Task<Void, Never>? = nil

    @MainActor
    func start() {
        task = Task { [weak self] in
            guard let self = self else { return }
            do {
                while !Task.isCancelled {
                    try await Task.sleep(nanoseconds: 1_000_000_000)
                    await MainActor.run {
                        self.time += 1
                        print("Timer: \(self.time)")
                    }
                }
            } catch {
                print("Timer task cancelled or encountered an error.")
            }
        }
    }

    func stop() {
        task?.cancel()
        task = nil
    }

}

struct TimerView: View {

    @EnvironmentObject var viewModel: TimerViewModel

    var body: some View {
        VStack {
            Text("Time is \(viewModel.time)")
            NavigationLink(value: viewModel.time, label: { Text("Next") })
        }
    }
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.