I tried to add two NavigationLink in one VStack, but it report a unknown error for me.
struct LoginView: View {
#State private var selector = ""
var body: some View {
NavigationView {
VStack(spacing: 30) { // error `Unable to infer complex closure return type; add explicit type to disambiguate`
NavigationLink(destination: OneView(), tag: "one",
selection: $selector) { EmptyView() }
NavigationLink(destination: TwoView(), tag: "two",
selection: $selector) { EmptyView() }
Button("change to view") {
self.selector = "one"
}
}
.navigationBarTitle("Test Navigation")
}
}
}
The selection by signature should be optional, so here is fix
struct LoginView: View {
#State private var selector: String? = "" // << here
// ... other your code
Related
For the purpose of this question, I have provided minimum sample code to re-create the bug. Just copy/paste it to run.
import SwiftUI
final class Popper: ObservableObject {
#Published var shouldProceed: String? = nil
var id: String
init(id: String) { self.id = id }
}
struct ContentView: View {
#StateObject var popper = Popper(id: "ROOT")
var body: some View {
NavigationView {
VStack(spacing: 40) {
Text("You are now in ROOT").font(.largeTitle)
Text("Tap Here to goto V1").onTapGesture {
popper.shouldProceed = "y"
}.foregroundColor(.blue)
NavigationLink(destination: V1().environmentObject(popper),
tag: "y",
selection: $popper.shouldProceed) {
EmptyView()
}
}
}
}
}
struct V1: View {
#EnvironmentObject var rootPopper: Popper
#StateObject var v1Popper = Popper(id: "V1")
var body: some View {
VStack(spacing: 40) {
Text("You are now in V1").font(.largeTitle)
Text("Tap Here to pop to root").onTapGesture {
print("popping to: \(rootPopper.id)")
rootPopper.shouldProceed = nil
}.foregroundColor(.red)
Text("Or tap here to go to V2").onTapGesture {
v1Popper.shouldProceed = "y"
}.foregroundColor(.blue)
NavigationLink(destination: V2().environmentObject(v1Popper),
tag: "y",
selection: $v1Popper.shouldProceed) {
EmptyView()
}
}
}
}
struct V2: View {
#EnvironmentObject var v1Popper: Popper
#State var shouldProceed: String? = nil
var body: some View {
VStack(spacing: 40) {
Text("You are now in V2").font(.largeTitle)
Text("Tap Here to pop to V1").onTapGesture {
print("popping to: \(v1Popper.id)")
v1Popper.shouldProceed = nil
}.foregroundColor(.red)
Text("Or Tap here to gotoV3").onTapGesture {
shouldProceed = "y"
}.foregroundColor(.blue)
NavigationLink(destination: V3().environmentObject(v1Popper),
tag: "y", selection: $shouldProceed) {
EmptyView()
}
}
}
}
struct V3: View {
#EnvironmentObject var v1Popper: Popper
var body: some View {
VStack(spacing: 40) {
Text("You are now in V3").font(.largeTitle)
Text("Tap Here to pop to V1").onTapGesture {
print("popping to: \(v1Popper.id)")
v1Popper.shouldProceed = nil
}.foregroundColor(.red)
}
}
}
Question When you get to screen V3, why does it not pop to the V1 screen? All the other pop functions work. It just fails for some reason when it gets to screen V3.
Please help.
Try to use .isDetailLink(false) for your links, like
NavigationLink(destination: V1().environmentObject(popper),
tag: "y",
selection: $popper.shouldProceed) {
EmptyView()
}.isDetailLink(false) // << here !!
I have a navigation stack that's not quite working as desired.
From my main view, I want to switch over to a list view which for the sake of this example represents an array of strings.
I want to then navigate to a detail view, where I want to be able to change the value of the selected string.
I have 2 issues with below code:
on the very first keystroke within the TextField, the detail view is being dismissed
the value itself is not being changed
Also, I suppose there must be a more convenient way to do the binding in the detail view ...
Here's the code:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
TestMainView()
}
}
}
struct TestMainView: View {
var body: some View {
NavigationView {
List {
NavigationLink("List View", destination: TestListView())
}
.navigationTitle("Test App")
}
}
}
struct TestListView: View {
#State var strings = [
"Foo",
"Bar",
"Buzz"
]
#State var selectedString: String? = nil
var body: some View {
List(strings.indices) { index in
NavigationLink(
destination: TestDetailView(selectedString: $selectedString),
tag: strings[index],
selection: $selectedString) {
Text(strings[index])
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("List")
}
}
}
struct TestDetailView: View {
#Binding var selectedString: String?
var body: some View {
VStack {
if let _ = selectedString {
TextField("Placeholder",
text: Binding<String>( //what's a better solution here?
get: { selectedString! },
set: { selectedString = $0 }
)
)
.padding()
.textFieldStyle(RoundedBorderTextFieldStyle())
}
Spacer()
}
.navigationTitle("Detail")
}
}
struct TestMainView_Previews: PreviewProvider {
static var previews: some View {
TestMainView()
}
}
I am quite obviously doing it wrong, but I cannot figure out what to do differently...
You're changing the NavigationLink's selection from inside the NavigationLink which forces the TestListView to reload.
You can try the following instead:
struct TestListView: View {
#State var strings = [
"Foo",
"Bar",
"Buzz",
]
var body: some View {
List(strings.indices) { index in
NavigationLink(destination: TestDetailView(selectedString: self.$strings[index])) {
Text(self.strings[index])
}
}
}
}
struct TestDetailView: View {
#Binding var selectedString: String // remove optional
var body: some View {
VStack {
TextField("Placeholder", text: $selectedString)
.padding()
.textFieldStyle(RoundedBorderTextFieldStyle())
Spacer()
}
}
}
I am trying to have two NavigationLinks inside a struct that I use as a NavigationLink inside a List with ForEach but the code does not work (the navigation does not happen to either View) , here is the code:
struct mainView : View {
#State var people = [Person(name: "one", family: "1", type: "men"),Person(name: "two", family: "2", type: "women")]
var body : some View {
List{
ForEach(self.people, id:\.self){person in
NavigationLink(destination: Text("hi")) {
PersonView(person : person)
}.buttonStyle(BorderlessButtonStyle())
}
}
}
struct Person {
var name : String
var family : String
var type : String
}
struct PersonView : View {
#State var person : Person?
var body : some View {
HStack{
NavigationLink(destination: Text(self.person.name)) {
Text("this is \(self.person.name) \(self.person.family)")
}
NavigationLink(destination: Text(self.person.type)) {
Text(self.person.type)
}
}
}
}
I have added the .buttonStyle after reading it might help but it did nothing. I also tried the following PersonView :
struct PersonView : View {
#State var person : Person?
#State var goToFirst = false
#State var goToType = false
var body : some View {
VStack{
Button(action:{
self.goToFirst.toggle()
}){
Text("this is \(self.person.name) \(self.person.family)")
}.buttonStyle(BorderlessButtonStyle())
Button(action:{
self.goToType.toggle()
}){
Text(self.person.type)
}.buttonStyle(BorderlessButtonStyle())
NavigationLink(destination: Text(self.person.name), isActive : self.$goToFirst) {
Text("").frame(width: 0.01, height: 0.01)
}
NavigationLink(destination: Text(self.person.type), isActive : self.$goToType) {
Text("").frame(width: 0.01, height: 0.01)
}
}
}
This did not work as well, what am I supposed to do so when I click anywhere on the cell it will navigate to Text("hi") and when I click on the respective areas it will navigate to the proper View .
Important: This view is getting navigated to from a view that is wrapped with a NavigationView
You have not added a navigationView, that is the problem.
struct MainView: View {
#State private var peoples = [Person(name: "one", family: "1", type: "men"),Person(name: "two", family: "2", type: "women")]
var body: some View {
NavigationView { //This is important
List {
ForEach(self.peoples, id: \.name) { people in
NavigationLink(destination: PersonView(person: people)) {
Text(people.name)
}
}
}
.navigationTitle("Demo")
}
}
}
struct Person {
var name : String
var family : String
var type : String
}
struct PersonView : View {
#State var person : Person?
var body : some View {
HStack{
NavigationLink(destination: Text(self.person.name)) {
Text("this is \(self.person.name) \(self.person.family)")
}
NavigationLink(destination: Text(self.person.type)) {
Text(self.person.type)
}
}
}
}
I think the following does what you want. The code can be pasted into an empty project.
Some clarifications:
The List seems to make things more difficult, so I replaced it with a VStack. You may need to surround it with a ScrollView.
Also, like user Asperi hinted, this works best when you create multiple NavigationLink but around an EmptyView that is then later activated.
struct MainView: View {
#State var people = [Person(name: "one", family: "1", type: "men"), Person(name: "two", family: "2", type: "women")]
var body: some View {
VStack {
ForEach(self.people, id: \.self) { person in
PersonView(person: person)
}
}
}
}
struct Person: Hashable {
var name: String
var family: String
var type: String
}
struct PersonView: View {
let person: Person
#State private var selection: Int?
var body: some View {
HStack {
NavigationLink(
destination: Text(self.person.name),
tag: self.person.hashValue + 1,
selection: self.$selection) {
EmptyView()
}
NavigationLink(
destination: Text(self.person.type),
tag: self.person.hashValue + 2,
selection: self.$selection) {
EmptyView()
}
Button("this is \(self.person.name) \(self.person.family)") {
self.selection = self.person.hashValue + 1
}
Spacer()
Button(self.person.type) {
self.selection = self.person.hashValue + 2
}.padding()
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
MainView()
}
}
}
For the purpose of this question, I have provided minimum sample code to re-create the bug. Just copy/paste it to run.
import SwiftUI
final class Popper: ObservableObject {
#Published var shouldProceed: String? = nil
var id: String
init(id: String) { self.id = id }
}
struct ContentView: View {
#StateObject var popper = Popper(id: "ROOT")
var body: some View {
NavigationView {
VStack(spacing: 40) {
Text("You are now in ROOT").font(.largeTitle)
Text("Tap Here to goto V1").onTapGesture {
popper.shouldProceed = "y"
}.foregroundColor(.blue)
NavigationLink(destination: V1().environmentObject(popper),
tag: "y",
selection: $popper.shouldProceed) {
EmptyView()
}
}
}
}
}
struct V1: View {
#EnvironmentObject var rootPopper: Popper
#StateObject var v1Popper = Popper(id: "V1")
var body: some View {
VStack(spacing: 40) {
Text("You are now in V1").font(.largeTitle)
Text("Tap Here to pop to root").onTapGesture {
print("popping to: \(rootPopper.id)")
rootPopper.shouldProceed = nil
}.foregroundColor(.red)
Text("Or tap here to go to V2").onTapGesture {
v1Popper.shouldProceed = "y"
}.foregroundColor(.blue)
NavigationLink(destination: V2().environmentObject(v1Popper),
tag: "y",
selection: $v1Popper.shouldProceed) {
EmptyView()
}
}
}
}
struct V2: View {
#EnvironmentObject var v1Popper: Popper
#State var shouldProceed: String? = nil
var body: some View {
VStack(spacing: 40) {
Text("You are now in V2").font(.largeTitle)
Text("Tap Here to pop to V1").onTapGesture {
print("popping to: \(v1Popper.id)")
v1Popper.shouldProceed = nil
}.foregroundColor(.red)
Text("Or Tap here to gotoV3").onTapGesture {
shouldProceed = "y"
}.foregroundColor(.blue)
NavigationLink(destination: V3().environmentObject(v1Popper),
tag: "y", selection: $shouldProceed) {
EmptyView()
}
}
}
}
struct V3: View {
#EnvironmentObject var v1Popper: Popper
var body: some View {
VStack(spacing: 40) {
Text("You are now in V3").font(.largeTitle)
Text("Tap Here to pop to V1").onTapGesture {
print("popping to: \(v1Popper.id)")
v1Popper.shouldProceed = nil
}.foregroundColor(.red)
}
}
}
Question When you get to screen V3, why does it not pop to the V1 screen? All the other pop functions work. It just fails for some reason when it gets to screen V3.
Please help.
Try to use .isDetailLink(false) for your links, like
NavigationLink(destination: V1().environmentObject(popper),
tag: "y",
selection: $popper.shouldProceed) {
EmptyView()
}.isDetailLink(false) // << here !!
I am trying to implement a List with Multiple selection.
This is the Code:
import SwiftUI
struct MultiSelectionWithEditButton : View {
var items = ["One","Two", "Three"]
#State var selectedRows = Set<String>()
var body: some View {
NavigationView {
List((items, id: \.self), selection: $selectedRows) { name in
Text(name)
.font(.title)
.fontWeight(.bold)
}
.navigationBarItems(trailing: EditButton())
.navigationBarTitle(Text("Selected \(selectedRows.count) rows"))
}
}
}
#if DEBUG
struct MultiSelectionWithEditButton_Previews : PreviewProvider {
static var previews: some View {
MultiSelectionWithEditButton()
}
}
#endif
But I get error bellow:
"Type of expression is ambiguous without more context"
Appreciate if anyone can help! : )
Problem is in yours Lists init. Change it to List with ForEach inside, like below:
struct MultiSelectionWithEditButton : View {
var items = ["One","Two", "Three"]
#State var selectedRows = Set<String>()
var body: some View {
NavigationView {
List(selection: $selectedRows){
ForEach(items, id: \.self) { name in
Text(name)
.font(.title)
.fontWeight(.bold)
}
}
.navigationBarItems(trailing: EditButton())
.navigationBarTitle(Text("Selected \(selectedRows.count) rows"))
}
}
}