SwiftUI - List / ForEach与NavigationLink和isActive结合使用不正常工作

13

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

我已经发现这与NavigationLink中的“isActive”属性有关。不幸的是,我需要它才能实现这里的行为:https://istack.dev59.com/g0BFz.gif,这也在这里SwiftUI - 嵌套的NavigationView:返回到根视图中提出。

我还尝试使用selection和tag属性,但我无法实现“返回到根”的机制。

这是示例:


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()
        }
    }
    
}

谢谢!

2个回答

18

这是由于只使用了一个状态navigationViewIsActive导致的。

因此,当您单击导航链接时,该值将更改为True,并且所有链接都将处于active状态。

此情况的解决方案如下:

  • 定义一个新的State,用于保存所选模型值
  • 您只需要一个NavigationLink,并使其Hidden(将其放在VStack中)
  • List中,使用Button而不是NavigationLink
  • 当单击Button时:首先更改selectedModel值,然后使导航链接处于活动状态(true)

就像下面的代码所示(已在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:我写了这篇文章:如何在SwiftUI中进行导航,它将帮助您了解在SwiftUI中导航的方式。


这修复了iOS 14.5中的一个错误 - 谢谢。 - KoCMoHaBTa
对我没用,反而崩溃了!我猜你的意思是将“navigationViewIsActive”绑定传递到第二个视图,然后从第二个视图设置为false。 - Michael Vescovo
还有一个绑定选择选项,但我也无法使其正常工作。由于某种原因,它运行非常缓慢,并且无论如何,我都无法在打开视图后将其关闭。 "init <V>(destination:Destination,tag:V,selection:Binding <V?>,label:() - > Label)where V:Hashable" - Michael Vescovo
谢谢 - 很棒的答案。我也想补充一下,在我的情况下,将触发点:self.navigationViewIsActive = true明确地包装在 withAnimation 块中有所帮助,因为有时候,传入的视图无法进行动画处理(显然是某些错误)。 - Konstantinos Kontos
你好,能展示一下具体的动画相关代码吗?我自己也遇到了同样的问题,第一个触发器没有显示动画,不过现在还不太清楚如何更改它。谢谢 @KonstantinosKontos - IDTIMW
这个可以工作,但是在列表项的第一次点击时似乎没有动画效果。第一次点击后,所有项目都有动画效果。 - J. Edgell

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,以使导航流崩溃。 - SwiftUIRookie
你的问题中没有提到关于这个期望行为的任何内容。 - Asperi
抱歉,我没有写得够准确。我想用我的问题中的这个陈述来表达这个要求:“我需要它实现这里的这种行为:https://i.stack.imgur.com/g0BFz.gif”。有任何想法是否可能实现? - SwiftUIRookie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接