SwiftUI 编程导航问题

时间:2021-07-04 15:04:50

标签: swift swiftui

我一直在尝试在我的 SwiftUI 代码中实现程序化导航,但收效甚微。我打算让下面的代码做的是检查用户是否填写了屏幕上的每个文本字段,如果是,则进入下一个屏幕。如果没有,则显示警报并允许用户返回并重新填充文本字段。然而,有一个障碍——如果用户在第一次尝试时完整填写表单,他们可能会进步,但如果没有,他们会收到警报(如预期的那样),但是一旦他们再次填写表单,点击 NavigationLink 就什么都不做,而它应该允许用户转到下一个屏幕。

// this part of the code defines the validation logic
@State private var textFieldsInvalid = false
@State private var lineupIsReady = false

/// this is the navigation link i'm trying to fix
NavigationLink(destination: SetHomeLineup(), isActive: self.$lineupIsReady) {
                    HStack {
                        Spacer()
                        Label("Submit", systemImage: "chevron.right.circle.fill")
                        Spacer()
                    }
                    .padding(.all, 18)
                    .background(Color.green)
                    .foregroundColor(.white)
                    .font(.title)
                    .onTapGesture {
                        for name in checkIfFieldsValid {
                            if !(name.isEmpty) {
                                self.lineupIsReady = true
                            }
                            else {
                                self.textFieldsInvalid = true
                            }
                        }
                    }
                }
/* this is the alert modifier attached to a VStack that contains a Form 
(with its text fields) and the navigation link above. I haven't included the Form 
because it's too large and not the problem area for now. */

            }
            .edgesIgnoringSafeArea(.bottom)
            .alert(isPresented: $textFieldsInvalid, content: {
                Alert(title: Text("Incomplete Information"), message: Text("Please make sure that you have filled in all fields."), dismissButton: .default(Text("Back")))

对于上下文,checkIfFieldsValid 是一个数组,用于存储九名玩家的姓名(由用户输入到文本字段中并存储在 @State private var 中)。我可以包含这些代码以及必要时的表单,但我很确定它们不会在错误中起作用,因为代码能够很好地读取和访问用户输入的名称。

谁能指出我的代码中导致错误的区域?我是初学者,只有两周的代码经验,因此不胜感激 :)

1 个答案:

答案 0 :(得分:0)

如果删除此行:

self.textFieldsInvalid = true

... 然后 NavigationLink 每次都会出现,只要您的文本字段之一不为空。这是因为您从未将 lineupIsReady 设置为 false。

for name in checkIfFieldsValid {
    if !(name.isEmpty) {
        self.lineupIsReady = true /// as long as one of the names aren't empty, you set it to true
    } else {
//        self.textFieldsInvalid = true (this line deleted for now)
    }
}

即使您的所有文本字段都为空,除了一个,lineupIsReady 仍会设置为 true,您的 NavigationLink 将触发。

您不能在循环中设置 lineupIsReady,因为您需要遍历所有文本字段以确定整个内容是否有效。相反,您需要在循环结束后设置它。

var lineupReady = true /// by default, set it to true
for name in checkIfFieldsValid {
    if name.isEmpty {
        lineupReady = false /// if just 1 text field is empty, set `lineupReady` to false
        break /// you can exit the loop now, because there's no way `lineupReady` can be set to true again
    }
}
self.lineupIsReady = lineupReady /// will trigger the NavigationLink

好的,这是 1 个问题。现在警报呢?让我们再看看你的旧代码:

for name in checkIfFieldsValid {
    if !(name.isEmpty) {
        self.lineupIsReady = true
    } else {
        self.textFieldsInvalid = true /// triggers the alert
    }
}

当文本字段为空时,行 self.textFieldsInvalid = true 会显示警报......这可能是多次。警报还会中断 NavigationLink,这就是它没有出现的原因。在循环完成后,您也应该只设置一次。

/// add this after `self.lineupIsReady = lineupReady`
... 

if !lineupReady {
    textFieldsInvalid = true /// will trigger the alert
}

完整代码:

struct ContentView: View {
    @State private var textFieldsInvalid = false /// for the alert
    @State private var lineupIsReady = false /// for the NavigationLink
    @State private var checkIfFieldsValid = ["Text", "", "More text"]
    
    var body: some View {
        NavigationView {
            VStack {
                ForEach(checkIfFieldsValid.indices, id: \.self) { index in
                    TextField("", text: $checkIfFieldsValid[index])
                        .border(Color.blue)
                }
                
                NavigationLink(destination: Text("Home lineup"), isActive: self.$lineupIsReady) {
                    HStack {
                        Spacer()
                        Label("Submit", systemImage: "chevron.right.circle.fill")
                        Spacer()
                    }
                    .padding(.all, 18)
                    .background(Color.green)
                    .foregroundColor(.white)
                    .font(.title)
                    .onTapGesture {
                        var lineupReady = true /// by default, set it to true
                        for name in checkIfFieldsValid {
                            if name.isEmpty {
                                lineupReady = false /// if just 1 text field is empty, set `lineupReady` to false
                                break /// you can exit the loop now, because there's no way `lineupReady` can be set to true again
                            }
                        }
                        self.lineupIsReady = lineupReady /// will trigger the NavigationLink
                        
                        if !lineupReady {
                            textFieldsInvalid = true /// will trigger the alert
                        }
                    }
                }
            }
        }
        .edgesIgnoringSafeArea(.bottom)
        .alert(isPresented: $textFieldsInvalid, content: {
            Alert(title: Text("Incomplete Information"), message: Text("Please make sure that you have filled in all fields."), dismissButton: .default(Text("Back")))
        })
    }
}

结果:

Alert shows if at least 1 textfield is empty, but if all are valid, then the NavigationLink presents