How to Access an About view via Navigation Bar Button - swiftui

How do you link an About navigation bar button to an About View? I have seen a number of posts on how to add a button to the navigation bar, but nothing on how to handle a link to the view.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: m1View()) {
Text("Menu 1")
}
.padding()
NavigationLink(destination: m2View()) {
Text("Menu 2")
}
.padding()
NavigationLink(destination: m3View()) {
Text("Menu 3")
}
.padding()
}
.font(.title)
.navigationBarTitle("Main View", displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
// TODO: add about link here
}) {
Text("About")
}
)
.onAppear(perform: loadDataViaBankApi)
}
}
func loadDataViaBankApi() -> () {
...
}
}

Here is possible solution - in navigation bar only activate link, but place link inside navigation view
struct ContentView: View {
#State private var activateAbout = false
var body: some View {
NavigationView {
VStack {
// ... other content here
}
.font(.title)
.navigationBarTitle("Main View", displayMode: .inline)
.background(
// hide programmatically activated link here !!
NavigationLink(destination: AboutView(), isActive: $activateAbout) { EmptyView() }
)
.navigationBarItems(trailing: Button(action: {
// not add - only activate it here, otherwise it will not work
self.activateAbout = true
}) {
Text("About")
}
)
...

Related

how to hide Tabview when using ToolbarItem, preserving ToolbarItem by disappearing

I have two views embedded in a TabView and a third view activated by a ToolbarItem in a navigationStack.
problem 1)
When I tap on plus button I navigate to my addView, but I still can see the tabs at the bottom.
problem 2)
after many test I found that if put the tabView code in MainView Inside a NavigationStack, I solve problem 1) but each time I dismiss from a detailView from a row in ContentView, the navigation Item disappears.
the main view for the tabview
struct MainView: View {
var body: some View {
TabView {
ContentView()
.tabItem {
Label("List", systemImage: "list.dash")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape.fill")
}
}
}
}
the ContentView (a list of lessons, navigationDestination goes to a detail view)
struct ContentView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest (sortDescriptors: [
SortDescriptor(\.lessonNuber, order: .reverse)
], predicate: nil) var lessons: FetchedResults<Lesson>
#State var showAddView = false
var body: some View {
NavigationStack {
VStack {
List {
ForEach(lessons, id: \.self) { lesson in
NavigationLink {
DetailView(lesson: lesson)
} label: {
HStack {
Text("\(lesson.lessonNuber)")
.font(.title)
Text( "\(lesson.un_notion)")
.font(.body)
}
}
}
}
// .background(
// NavigationLink(destination: AddView(), isActive: $showAddView) {
// AddView()
// }
// )
.navigationDestination(isPresented: $showAddView) {
AddView()
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
showAddView = true
} label: {
Label("Add Lesson", systemImage: "plus")
}
}
}
}
.padding()
}
}
}

NavigationLink within view presented as an overlay. SwiftUI

I am presenting a view as an overlay but when I add a NavigationLink with a destination to that overlay view, the text is greyed out and tapping it does nothing. How do I proceed?
struct ContentView: View {
var body: some View {
NavigationView { }
.overlay(
VStack {
NavigationLink(destination: Text("Go to some view")) {
Text("NavigationLink in overlay")
}
Button {
print("button tapped")
} label: {
Text("Button in overlay")
}
}
)
}
}
Your NavigationLink is not inside the NavigationView! Try this:
struct ContentView: View {
var body: some View {
NavigationView {
Color.clear
.overlay(
VStack {
NavigationLink(destination: Text("Go to some view")) {
Text("NavigationLink in overlay")
}
Button {
print("button tapped")
} label: {
Text("Button in overlay")
}
}
)
}
}
}

SwiftUI: Hidden NavigationBar blocks UI

I am using a NavigationView to navigate between views. As I don't want to use the NavigationBar, I am hiding it. Unfortunately, the area where the NavigationBar was (it is hidden) is still blocking the UI. A button there cannot be tapped.
How can I fix this?
// First View
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
Text("Title")
Spacer()
NavigationLink(destination: View2()) {
Text("Next")
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
Spacer()
}
}
}
}
and
// Second View: Here the button cannot be tapped
struct View2: View {
var body: some View {
NavigationView {
VStack {
Button(action: {
print("this button doesn't work")
}, label: {
Text("Do something")
})
Spacer()
}
.padding(.top, 50)
.edgesIgnoringSafeArea(.all)
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
The issue is due to used second NavigationView - it is wrong, there should be only one root navigation view which manages navigation stack in this case.
So here is fixed view (child one)
struct View2: View {
var body: some View {
VStack {
Button(action: {
print("this button doesn't work")
}, label: {
Text("Do something")
})
Spacer()
}
.padding(.top, 50)
.edgesIgnoringSafeArea(.all)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Tested with Xcode 13.2 / iOS 15.2
Note: as you hide navigation but you don't need mode modifier as well, so removed .navigationBarTitle("", displayMode: .inline). Just for your info.

How to create custom Navigation Bar Swift UI?

I've been using default navigation bar (because it has the ability to enable swipes to close a View), but since my issue is to hide NavBar in a RootView and show when it disappears after Navigation to a ChildView, I faced a problem with my ChildView (it bounce up and down after manipulations with navbar). Hence I need a custom NavBar (perfectly would be with an ability to make swipes to hide it.)
Here you can see my code and issue with NavBar that was solved and triggered the one you are reading.
My RootView
struct ExploreView: View {
var body: some View {
ZStack{
VStack{
HStack{
NavigationLink(destination: MessagesView()){
Image("messages")
}
}
}
}
}.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
ChildView#
struct MessagesView: View {
#Environment(\.presentationMode) var presentationMode
var btnBack : some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "chevron.left")
.font(.title)
}
}
var body: some View {
ZStack{
VStack{
Spacer()
HStack {
btnBack
.padding(.leading, 10)
Spacer()
Button(action:{
self.show.toggle()
},label: {
Image("writemessage")
.foregroundColor(Color("blackAndWhite"))
}
)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
A custom NavigationBar could look like this. Of course it can be individualized with colors and fontSizes etc. in whatever way you like:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
HStack {
NavigationLink(destination: MessagesView()){
Text("Go to MessagesView")
}
}
}.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
struct MessagesView: View {
#Environment(\.presentationMode) var presentationMode
var btnBack : some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "chevron.left")
.font(.title)
}
}
var body: some View {
ZStack{
VStack{
HStack {
btnBack
.padding(.leading, 10)
Spacer()
}
Spacer()
Text("MessagesView")
Spacer()
}.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
To keep the Swipe-back gesture working even while the standard NavigationBar is disabled you need some addition under your SceneDelegate:
extension UINavigationController: UIGestureRecognizerDelegate {
override open func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}

SwiftUI ContextMenu navigation to another view

I am trying to get a context menu to navigate to another view using the following code
var body: some View
{
VStack
{
Text(self.event.name).font(.body)
...
Spacer()
NavigationLink(destination: EditView(event: self.event))
{
Image(systemName: "pencil")
}
}
.navigationBarTitle(Text(appName))
.contextMenu
{
NavigationLink(destination: EditView(event: self.event))
{
Image(systemName: "pencil")
}
}
}
The NavigationLink within the VStack works as expected and navigates to the edit view but I want to use a contextMenu. Although the context menu displays the image, when I tap on it it doesn't navigate to the edit view, instead it just cancels the context menu.
I am doing this within a watch app but don't think that should make a difference, is there anything special I have to do with context menu navigation?
I would use the isActive variant of NavigationLink that you can trigger by setting a state variable. Apple documents this here
This variant of NavigationLink is well fit for dynamic/programatic navigation.
Your .contextMenu sets the state variable to true and that activates the NavigationLink. Because you don't want the link to be visible, set the label view to EmptyView
Here's an example, not identical to your post but hopefully makes it clear.
struct ContentView: View {
#State private var showEditView = false
var body: some View {
NavigationView {
VStack {
Text("Long Press Me")
.contextMenu {
Button(action: {
self.showEditView = true
}, label: {
HStack {
Text("Edit")
Image(systemName: "pencil")
}
})
}
NavigationLink(destination: Text("Edit Mode View Here"), isActive: $showEditView) {
EmptyView()
}
}
.navigationBarTitle("Context Menu")
}
}
}
In Xcode 11.4 it's now possible to do this with sensible NavigationLink buttons. Yay! 🎉
.contextMenu {
NavigationLink(destination: VisitEditView(visit: visit)) {
Text("Edit visit")
Image(systemName: "square.and.pencil")
}
NavigationLink(destination: SegmentsEditView(timelineItem: visit)) {
Text("Edit individual segments")
Image(systemName: "ellipsis")
}
}
This works on Xcode 11.6
struct ContentView: View {
#State var isActiveFromContextMenu = false
var body: some View {
NavigationView{
VStack{
NavigationLink(destination : detailTwo(), isActive: $isActiveFromContextMenu ){
EmptyView()
}
List{
NavigationLink(destination: detail() ){
row(isActiveFromContextMenu: $isActiveFromContextMenu)
}
NavigationLink(destination: detail() ){
row(isActiveFromContextMenu: $isActiveFromContextMenu)
}
NavigationLink(destination: detail() ){
row(isActiveFromContextMenu: $isActiveFromContextMenu)
}
}
}
}
}
}
struct detail: View {
var body: some View{
Text("Detail view")
}
}
struct detailTwo: View {
var body: some View{
Text("DetailTwo view")
}
}
struct row: View {
#Binding var isActiveFromContextMenu : Bool
var body: some View {
HStack{
Text("item")
}.contextMenu{
Button(action: {
self.isActiveFromContextMenu = true
})
{
Text("navigate to")
}
}
}
}
I found success in masking the NavigationLink in the background and switching the context with a Button as the shortest yet simplest alternative.
struct ContentView: View {
#State private var isShowing = false
var body: some View {
NavigationView {
Text("Hello")
.background(NavigationLink("", destination: Text("World!"), isActive: $isShowing))
.contextMenu {
Button {
isShowing = true
} label: {
Label("Switch to New View", systemImage: "chevron.forward")
}
}
}
}
}