适用于iOS15.0+的NavigationStack

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

我正在尝试创建一种协调器,在导航时堆叠视图并使其与 iOS 15.0 兼容。 这是我的代码,适用于第一次推送到下一个视图,但进入第三个视图后,它返回到第一个视图...... 我愿意接受任何建议,但我必须保持 iOS15 的兼容性。 这是我的代码:

import SwiftUI
import Combine

final class FlowCoordinator: ObservableObject {
    
    @Published var selection: String?
    
    private var cancellables = Set<AnyCancellable>()

    init() {
        addOberver()
    }
    
    func showView(_ view: String) {
        selection = view
    }
    
    func addOberver() {
        $selection
            .sink { newString in
            print(newString ?? "")
        }
        .store(in: &cancellables)
    }
    
    func repeatPrints() {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            print(self.selection ?? "")
        }

    }
}

struct ContentView: View {
    @StateObject var coordinator = FlowCoordinator()

    var body: some View {
        NavigationView {
            MainListScreen()
                .navigationTitle("Main List")
        }
        .environmentObject(coordinator)
        .navigationViewStyle(StackNavigationViewStyle())
    }
    
}

struct MainListScreen: View {
    @EnvironmentObject var coordinator: FlowCoordinator

    var body: some View {
        VStack {
            NavigationLink(destination: MealDetailsScreen(),
                           tag: "MealDetailsScreen",
                           selection: $coordinator.selection) {
                EmptyView()
            }

            Button(action: {
                coordinator.showView("MealDetailsScreen")
            }) {
                Text("Go to Meal Details")
            }
        }
    }
}

struct MealDetailsScreen: View {
    @EnvironmentObject var coordinator: FlowCoordinator

    var body: some View {
        VStack {
            NavigationLink(destination: MealMultimediaScreen(),
                           tag: "MealMultimediaScreen",
                           selection: $coordinator.selection) {
                EmptyView()
            }

            Button(action: {
                coordinator.showView("MealMultimediaScreen")
            }) {
                Text("Go to Meal Multimedia")
            }
        }
        .navigationTitle("Meal Details")
    }
}

struct MealMultimediaScreen: View {
    @EnvironmentObject var coordinator: FlowCoordinator
    
    var body: some View {
        VStack {
            
            NavigationLink(destination: Text("Last View"),
                           tag: "Text",
                           selection: $coordinator.selection) {
                EmptyView()
            }

            Button(action: {
                coordinator.showView("Text")
            }) {
                Text("Go to Text")
            }
        }
        .navigationTitle("Meal Multimedia")
    }
    
}

#Preview {
    ContentView()
}

ios swiftui swiftui-navigationlink ios15 coordinator-pattern
1个回答
0
投票

当您推送第三个视图时,

coordinator.selection
会更新为
MealMultimediaScreen
,这与第一个链接的标签
MealDetailsScreen
不匹配并弹出视图。

这种带有标签和选择的导航方法仅适用于单个视图,因为一个视图无法同时导航到多个目的地,并且它需要您为每个目的地都有一个单独的

FlowCoordinator
View

我建议您使用

NavigationLink(isActive:
,因为您可以基于堆栈操作
isActive
参数:

final class FlowCoordinator: ObservableObject {
    @Published var stack = [String]()
    private var cancellables = Set<AnyCancellable>()
    private func pop(to tag: String) {
        var lastTag: String?
        while lastTag != tag {
            lastTag = stack.popLast()
        }
    }
    func isActive(for tag: String) -> Binding<Bool> {
        return Binding {
            return self.stack.contains(tag)
        } set: { newValue in
            if !newValue {
                self.pop(to: tag)
            }else {
                self.stack.append(tag)
            }
        }
    }
    func push(_ tag: String) {
        stack.append(tag)
    }
    func pop() {
        _ = stack.popLast()
    }
}

然后创建一个处理协调器的

NavigationLink
包装器:

struct NavigationLinkView<Content: View>: View {
    @EnvironmentObject private var coordinator: FlowCoordinator
    private let destination: Content
    private let tag: String
    init(tag: String, destination: Content) {
        self.destination = destination
        self.tag = tag
    }
    var body: some View {
        NavigationLink(isActive: coordinator.isActive(for: tag)) {
            destination
        } label: {
            EmptyView()
        }
    }
}

用途:

NavigationLink(tag: "MealDetailsScreen", destination: MealDetailsScreen())
© www.soinside.com 2019 - 2024. All rights reserved.