How to move between views in SwiftUI? - swiftui

I am trying to open a view on a button click rather than on clicking a Navigation Link, but all I can find on the internet is how to do it with a Navigation Link. So how do you do it without?
Code:
#State var goToMainView = false
NavigationLink(destination: ContentView(),isActive: $goToMainView,label:{}).hidden()
Button(action: {
goToMainView = true
context.delete(goal)
do {
try context.save()
} catch {
print(error)
}
}, label: {
Text("Delete Goal")
})

You can navigate with a button by using NavigationLink
#State var goToSecondView = false
NavigationView {
VStack {
NavigationLink(destination: SecondView(),isActive: $goToSecondView,
label: { EmptyView() })
}.hidden()
Button(action : { goToSecondView = true } , label : { Text("Click On Me") })
}
}
And for more details , I wrote this article , it may help you
How to navigate with SwiftUI

Related

Adding a Navigation Link inside a menu [duplicate]

Can you use a NavigationLink as a Menu's item in swiftUI?
It seems to do just nothing:
Menu {
NavigationLink(destination: Text("test1")) {
Text("item1")
}
NavigationLink(destination: Text("test2")) {
Text("item2")
}
} label: {
Text("open menu")
}
In case it is meant to not work as tried above, is there an alternative way of achiving the intended reult?
init(destination:isActive:label:) is deprecated since iOS 16
'init(destination:isActive:label:)' was deprecated in iOS 16.0: use
NavigationLink(value:label:) inside a NavigationStack or
NavigationSplitView
NavigationLink should be inside NavigationView hierarchy. The Menu is outside navigation view, so put buttons inside menu which activate navigation link placed inside navigation view, eg. hidden in background.
Here is a demo of possible approach (tested with Xcode 12.1 / iOS 14.1)
struct DemoNavigateFromMenu: View {
#State private var navigateTo = ""
#State private var isActive = false
var body: some View {
NavigationView {
Menu {
Button("item1") {
self.navigateTo = "test1"
self.isActive = true
}
Button("item2") {
self.navigateTo = "test2"
self.isActive = true
}
} label: {
Text("open menu")
}
.background(
NavigationLink(destination: Text(self.navigateTo), isActive: $isActive) {
EmptyView()
})
}
}
}
I can say that Asperi's answer is great solution. It helped a lot. But we need a custom view to hold a reference inside the destination property right? not a string.
#State var navigateTo: AnyView?
#State var isNavigationActive = false
We can hold a reference AnyView type and then call the view like this:
Menu {
Button {
navigateTo = AnyView(CreateItemView())
isNavigationActive = true
} label: {
Label("Create an Item", systemImage: "doc")
}
Button {
navigateTo = AnyView(CreateItemView())
isNavigationActive = true
} label: {
Label("Create a category", systemImage: "folder")
}
} label: {
Label("Add", systemImage: "plus")
}
For more detail please see this post:
https://developer.apple.com/forums/thread/119583

Multiple sheet(item: ) triggered by SwipeActions button and ToolBar buttons gets nil object in the first time in SwiftUI

I am using swipeActions in ForEach loop of a List and toolbar buttons to show different sheets on my SwiftUI view. But every first time I swipe left and click the Edit button, the object of that line is nil. If I do the same swipe and click thing again, everything goes well. Anyone else had this kind of bug before? Thank you.
Here is the related code:
struct LanguagesView: View {
#State var activeSheet: ActiveSheet?
#State var toBeEdit: MeLanguage?
var body: some View {
NavigationView {
List {
ForEach(self.meLanguages, id: \.id) { lan in
HStack {
Text("\(lan.wrappedName)")
.font(.headline)
}.swipeActions(allowsFullSwipe: false) {
Button(
action: {
self.activeSheet = .editLanguage
self.toBeEdit = lan
},
label: { Label("Edit", systemImage: "pencil") }
) .tint(.indigo)
}
}
}
.sheet(item: $activeSheet,
onDismiss: {
self.toBeEdit = nil
}
){
item in
switch item {
case .addLanguage:
AddLanguage()
case .sortLanguages:
SortLanguagesView()
case .editLanguage:
if self.toBeEdit != nil {
EditLanguageView( meLanguage: self.toBeEdit! )
}
else {
Text("self.toBeEdit is nil")
}
default:
Text("No such button on ContentView.")
}
}
.toolbar {
ToolbarItemGroup {
HStack {
Text("\(self.meLanguages.count) Languages on Card").font(.headline)
self.barButtons
}
}
}
}
}
var barButtons: some View {
HStack {
Button(
action: {
self.activeSheet = .sortLanguages
},
label: { Label("Sort Languages", systemImage: "arrow.up.arrow.down.circle")
}
).id("sortLanguages")
Button(
action: {
self.activeSheet = .addLanguage
},
label: { Label("Add Language",
systemImage: "plus")
.imageScale(.large)
}
)
}
}
}
If I only think of the sheet triggered by swipeActions Edit button, the code below works perfectly. But I still need other sheets triggered by ToolBar buttons.
.sheet(item: self.$toBeEdit, content: { toBeEdit in
EditLanguageView( meLanguage: toBeEdit)
})
After more searching I realised it's not something about SwipeActions. This is actually similar to this question:
SwiftUI presenting sheet with Binding variable doesn't work when first presented
So I added an hidden Text after the ForEach loop and the problem solved.
ForEach(self.meLanguages, id: \.id) { ... }
if self.toBeEdit != nil {
Text("\(self.toBeEdit!.wrappedName)").hidden()
}

SwiftUI: How to push to new view from Menu Buttons [duplicate]

Can you use a NavigationLink as a Menu's item in swiftUI?
It seems to do just nothing:
Menu {
NavigationLink(destination: Text("test1")) {
Text("item1")
}
NavigationLink(destination: Text("test2")) {
Text("item2")
}
} label: {
Text("open menu")
}
In case it is meant to not work as tried above, is there an alternative way of achiving the intended reult?
init(destination:isActive:label:) is deprecated since iOS 16
'init(destination:isActive:label:)' was deprecated in iOS 16.0: use
NavigationLink(value:label:) inside a NavigationStack or
NavigationSplitView
NavigationLink should be inside NavigationView hierarchy. The Menu is outside navigation view, so put buttons inside menu which activate navigation link placed inside navigation view, eg. hidden in background.
Here is a demo of possible approach (tested with Xcode 12.1 / iOS 14.1)
struct DemoNavigateFromMenu: View {
#State private var navigateTo = ""
#State private var isActive = false
var body: some View {
NavigationView {
Menu {
Button("item1") {
self.navigateTo = "test1"
self.isActive = true
}
Button("item2") {
self.navigateTo = "test2"
self.isActive = true
}
} label: {
Text("open menu")
}
.background(
NavigationLink(destination: Text(self.navigateTo), isActive: $isActive) {
EmptyView()
})
}
}
}
I can say that Asperi's answer is great solution. It helped a lot. But we need a custom view to hold a reference inside the destination property right? not a string.
#State var navigateTo: AnyView?
#State var isNavigationActive = false
We can hold a reference AnyView type and then call the view like this:
Menu {
Button {
navigateTo = AnyView(CreateItemView())
isNavigationActive = true
} label: {
Label("Create an Item", systemImage: "doc")
}
Button {
navigateTo = AnyView(CreateItemView())
isNavigationActive = true
} label: {
Label("Create a category", systemImage: "folder")
}
} label: {
Label("Add", systemImage: "plus")
}
For more detail please see this post:
https://developer.apple.com/forums/thread/119583

SwiftUI multiple sheets: dismiss animation broken

I have button1, showing sheet1 when pressed. Then I have a toggle, showing a button2, which in turn presents sheet2 when pressed.
If I just press button1, the sheet shows and dismisses with animation. If, however, I press the toggle, which shows button2, and then press button1, the sheet dismiss animation is broken (simply doesn't animate).
struct ContentView: View {
#State private var showSheetButton2 = false
#State private var showSheet1 = false
#State private var showSheet2 = false
var body: some View {
VStack {
Toggle("Show Sheet Button 2", isOn: $showSheetButton2)
Button(action: {
showSheet1.toggle()
}, label: {
Text("Show Sheet 1")
})
.sheet(isPresented: $showSheet1, content: {
Button(action: {
showSheet1 = false
}, label: {
Text("Dismiss")
})
})
if showSheetButton2 {
Button(action: {
showSheet2.toggle()
}, label: {
Text("Show Sheet 2")
})
.sheet(isPresented: $showSheet2, content: {
Button(action: {
showSheet2 = false
}, label: {
Text("Dismiss")
})
})
}
}.padding()
}
}
EDIT:
Fixed in iOS 14.5:
You can now apply multiple sheet(isPresented:onDismiss:content:) and
fullScreenCover(item:onDismiss:content:) modifiers in the same view
hierarchy. (74246633)
Somehow this sheet inside If statement is destroying animation. Please try to add EmptyView outside this if statement and assign sheet to that view.
if showSheetButton2 {
Button(action: {
showSheet2.toggle()
}, label: {
Text("Show Sheet 2")
})
}
EmptyView()
.sheet(isPresented: $showSheet2, content: {
Button(action: {
showSheet2 = false
}, label: {
Text("Dismiss")
})
})

Using SwiftUI, we installed a Button in the List. Why does the modal disappear when I tap the button to display the modal and then close it again?

I am now learning to create a sample code for SwiftUI using the official version of Xcode11.
I wrote a simple code to show and hide modal.
This code adds a button to the list and displays a modal.
Strangely, however, the modal no longer appears when the button is tapped again after closing.
Is there a reason for this or any solution?
Occurs when there is a button in the list, but if you delete only the list from the code, the modal can be displayed as many times as you like.
This is the code that causes the bug.
struct ContentView: View {
#State var show_modal = false
var body: some View {
List {
Button(action: {
print("Button Pushed")
self.show_modal = true
}) {
Text("Show Modal")
}.sheet(isPresented: self.$show_modal, onDismiss: {
print("dismiss")
}) {
ModalView()
}
}
}
}
This is a code that does not cause a bug.
struct ContentView: View {
#State var show_modal = false
var body: some View {
Button(action: {
print("Button Pushed")
self.show_modal = true
}) {
Text("Show Modal")
}.sheet(isPresented: self.$show_modal, onDismiss: {
print("dismiss")
}) {
ModalView()
}
}
}
The only difference is whether or not there is a List.
The ModalView code is below.
struct ModalView: View {
// 1. Add the environment variable
#Environment(\.presentationMode) var presentationMode
var body: some View {
// 2. Embed Text in a VStack
VStack {
// 3. Add a button with the following action
Button(action: {
print("dismisses form")
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Dismiss")
}.padding(.bottom, 50)
Text("This is a modal")
}
}
}
When breakpoint is set, print ("Button Pushed") is called every time, but ModalView of .sheet is not called, and naturally the body of ModalView class is not called.
I think the issue is that your .sheet is not on the List itself but on the Button in your code that causes the bug.
Try this instead:
struct ContentView: View {
#State var show_modal = false
var body: some View {
List {
Button(action: {
print("Button Pushed")
self.show_modal = true
}) {
Text("Show Modal")
}
}.sheet(isPresented: self.$show_modal, onDismiss: {
print("dismiss")
}) {
ModalView()
}
}
}