【问题标题】:SwiftUI - List / ForEach in combination with NavigationLink and isActive doesn't work properlySwiftUI - List / ForEach 结合 NavigationLink 和 isActive 无法正常工作
【发布时间】:2021-03-31 12:47:15
【问题描述】:

我正在尝试在 SwiftUI 的 List 或 ForEach 循环中执行 NavigationLink。不幸的是,我遇到了一个非常奇怪的行为(例如,当点击 Leo 时会打开 Karl,Opening Max 也会指向 Karl)。

我已经发现它与 NavigationLink 中的“isActive”属性有关。不幸的是,我需要它来实现这种行为:https://i.stack.imgur.com/g0BFz.gif,这里也有问SwiftUI - Nested NavigationView: Go back to root

我也尝试使用选择和标签属性,但我无法实现“返回根”机制。

示例如下:


import SwiftUI


struct Model: Equatable, Hashable {
    var userId: String
    var firstName: String
    var lastName: String
}


struct ContentView: View {
    
    @State var navigationViewIsActive: Bool = false
    
    var myModelArray: [Model] = [
        Model(userId: "27e880a9-54c5-4da1-afff-05b4584b1d2f", firstName: "Leo", lastName: "Test"),
        Model(userId: "1050412a-cb12-4160-b7e4-2702ab8430c3", firstName: "Max", lastName: "Test"),
        Model(userId: "1050412a-cb12-4160-b7e4-2702ab8430c3", firstName: "Karl", lastName: "Test")]
    
    var body: some View {
        NavigationView {
            List(myModelArray, id: \.self) { model in
                NavigationLink(destination: secondView(firstName: model.firstName), isActive: $navigationViewIsActive){ Text(model.firstName) }
            }
            .listStyle(PlainListStyle())
        }
    }
}

struct secondView: View {
    
    @State var firstName: String
    
    var body: some View {
        NavigationView {
            Text(firstName)
                .padding()
        }
    }
    
}

谢谢!

【问题讨论】:

    标签: list foreach swiftui swiftui-navigationlink


    【解决方案1】:

    这是因为只使用了一个状态navigationViewIsActive

    所以当你点击一个导航链接时,值会变成True,所有的链接都是active

    这个场景的解决方案是这样的:

    • 定义一个新的State,它将保存选定的模型值
    • 您只需要一个 NavigationLink ,然后将其设为 Hidden (将其放入 VStack 中)
    • List 中使用Button 而不是NavigationLink
    • 点击Button 时:首先更改selectedModel 的值,然后激活navigationLink (true)

    像下面的代码(Tested with IOS 14):

    import SwiftUI
    
    
    struct Model: Equatable, Hashable {
        var userId: String
        var firstName: String
        var lastName: String
    }
    
    
    struct ContentView: View {
        
        @State var navigationViewIsActive: Bool = false
        @State var selectedModel : Model? = nil
        
        var myModelArray: [Model] = [
            Model(userId: "27e880a9-54c5-4da1-afff-05b4584b1d2f", firstName: "Leo", lastName: "Test"),
            Model(userId: "1050412a-cb12-4160-b7e4-2702ab8430c3", firstName: "Max", lastName: "Test"),
            Model(userId: "1050412a-cb12-4160-b7e4-2702ab8430c3", firstName: "Karl", lastName: "Test")]
        
        var body: some View {
            NavigationView {
                VStack {
                    VStack {
                        if selectedModel != nil {
                            NavigationLink(destination: SecondView(firstName: selectedModel!.firstName), isActive: $navigationViewIsActive){ EmptyView() }
                        }
                    }.hidden()
                    
                    List(myModelArray, id: \.self) { model in
                        Button(action: {
                            self.selectedModel = model
                            self.navigationViewIsActive = true
                        }, label: {
                            Text(model.firstName)
                        })
                    }
                    .listStyle(PlainListStyle())
                }
                
                
            }
        }
    }
    
    struct SecondView: View {
        
        @State var firstName: String
        
        var body: some View {
            NavigationView {
                Text(firstName)
                    .padding()
            }
        }
        
    }
    
    struct Test_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    PS:我写了这个:How to navigate with SwiftUI,它将帮助您了解在 swiftUI 中导航的方式

    【讨论】:

    • 这修复了 iOS 14.5 中的一个错误 - 谢谢
    • 对我不起作用。反而崩溃了!我假设您打算将“navigationViewIsActive”绑定传递到第二个视图,然后从第二个视图设置为 false。
    • 还有绑定选择选项,但我也无法使其正常工作。出于某种原因,它运行得非常慢,无论如何我无法让它在打开后关闭视图。 "init(destination: Destination, tag: V, selection: Binding, label: () -> Label) where V: Hashable"
    • 谢谢 - 很好的答案。还可以补充一点,在我的情况下,它有助于将触发点:self.navigationViewIsActive = true 明确地包装在 withAnimation 块内,因为有时,传入的视图无法动画(显然是一些错误)。
    【解决方案2】:

    在这种情况下你不需要 isActive,只需使用

    List(myModelArray, id: \.self) { model in
        NavigationLink(destination: secondView(firstName: model.firstName)) { 
           Text(model.firstName) 
        }
    }
    

    并且您没有在第二个视图中使用 NavigationView,即。

    struct secondView: View {
        
        var firstName: String   // you don't need state here as well
        
        var body: some View {
            Text(firstName)
                .padding()
        }
    }
    

    【讨论】:

    • 但是如果我在这种情况下不使用 isActive 我应该如何从我的第二个或第三个视图返回到初始列表?根据我的研究,正确的方法是使用 isActive ,然后从第三个视图将其设置为 false ,以便导航流程崩溃。
    • 您的问题中没有关于这种想要的行为。
    • 嗯,抱歉,写得不够准确。我想在我的问题“我需要它来实现这种行为:i.stack.imgur.com/g0BFz.gif”中用这个语句来表达这个要求。如果可能的话,有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    • 2020-05-05
    • 2020-11-21
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多