It is possible to make NavigationView title clickable in SwiftUI? - swiftui

I want to add action on tap to NavigationView title. Is it possible in SwiftUI?

If you can use the .inline title display mode, you can use the new toolbar API introduced in iOS 14 to overwrite the title displayed in the navigation bar.
See below a simple example, where tapping the title is incrementing the counter:
struct ContentView: View {
#State private var counter = 0
var body: some View {
NavigationView {
VStack {
Text("Main content")
Text("\(counter)")
.font(.title)
}
.navigationBarTitleDisplayMode(.inline)
.toolbar(content: {
ToolbarItem(placement: .principal) {
Text("Press here")
.bold()
.onTapGesture {
counter += 1
}
}
})
}
}
}

Related

SwiftUI how to align the navigation bar title on center when user taps on a navigation link?

I have a TabView in a struct. I add a NavigationView and a NavigationLink. When the view shows up the navigation bar is centered. When i tap on the navigation link the text is on the leading side. How can i make the navigation bar text align to center after i tap the navigation link in iOS 16 and above? I have searched stackoverflow and nothing seems to work in iOS 16 with this specific implementation i am posting below. Any help appreciated.
import Foundation
import SwiftUI
public struct TabViewTest: View {
#State var selectedTab: Int = 0
public var body: some View {
TabView (selection: $selectedTab) {
NavigationView {
NavigationLink(destination: Text("destination"))
{
FeedView(selectedTab: $selectedTab)
.navigationBarTitle("Feed", displayMode: .inline)
.toolbar {
ToolbarItemGroup {
Image(systemName :"magnifyingglass.circle")
.onTapGesture {
selectedTab = 1
}
}
}
}
}
.tabItem {
Text("Feed")
.foregroundColor(.white)
}
.tag(0)
}
}
struct ProfileView: View {
#Binding var selectedTab: Int
var body: some View {
ZStack {
Color.white.ignoresSafeArea()
VStack {
Text("Nav link test tap anywhere")
.font(.largeTitle)
.foregroundColor(.white)
}
}
}
}

Navigation Link Returning Back Two Levels on Exit

I am having trouble with a return from a navigation view within a tabbed view. My project has a Settings tab where the user may select via navigation link "View Entries". And from there another navigation link to "Add New Entry". Returning from Add New Entry should bring you to View Entries but instead is return another level to the Setting Menu.
I am seeing a warning on the console stating "trying to pop to a missing destination at /Library/Caches/com.apple...". Using the tabbed view sample code at SwiftUI NavigationView trying to pop to missing destination (Monoceros?) I no longer get the "pop-to-missing-destination" warning but I still have the same problem with the navigation return.
The sample code below is ready to run and test in Xcode 12.
In the sample code below, tap settings and select the navigation view "View Entries". This would be a screen where entries are displayed in a list. Tapping the plus button is where new entries could be added. The textfield on the "Add New Entry" screen doesn't do anything. Clicking the Save or Back buttons should return you to "View Entries" screen but instead returns you to the Setting Menu. The Save button uses presentationMode.wrappedValue.dismiss to dismiss the view.
The fact that two different version of the tab view logic didn't have any impact on my navigation view return logic leads me to believe that I just have some kind on plain old bug in my navigation view logic but I sure don't see one. The sample code below is using the standard tab view logic.
struct ContentView: View {
#State private var selection = 0
var body: some View {
NavigationView {
TabView (selection: $selection) {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}.tag(1)
AView()
.tabItem {
Label("A", systemImage: "a.circle")
}.tag(2)
BView()
.tabItem {
Label("B", systemImage: "b.circle")
}.tag(3)
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape")
}.tag(4)
}
}
}
}
struct HomeView: View {
var body: some View {
Text("Home Screen")
}
}
struct AView: View {
var body: some View {
Text("A Screen")
}
}
struct BView: View {
var body: some View {
Text("B Screen")
}
}
struct SettingsView: View {
var body: some View {
VStack (alignment: .leading) {
List {
Text("Settings")
.font(.title)
.fontWeight(.bold)
.padding(.leading, 15)
NavigationLink(destination: SetAView()) {Text("View Entries")}
}
}
.font(.body)
}
}
struct SetAView: View {
var body: some View {
List {
Text("View Entries")
.padding(.vertical, 10)
Text("Normally entires would be displayed here")
Text("Should return here upon adding new entry")
.padding(.vertical, 10)
Text("Click the + button to add new entry")
}
.navigationBarItems(trailing: NavigationLink (destination: AddTestView()) {
Image(systemName: "plus")
.resizable()
.foregroundColor(Color(.systemBlue))
.frame(width: 18, height: 18)
} // body
)
}
}
struct AddTestView: View {
#Environment(\.presentationMode) var presentationMode
#State private var catSelect: String = ""
var body: some View {
NavigationView {
VStack {
Form {
Section {
TextField("Enter Entry Name", text: $catSelect)
.padding(.horizontal, 20)
.keyboardType(.default)
}
}
}
.navigationBarTitle(Text("Add new Entry"), displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text ("Save")
})
}
}
}
After considerable analysis I discovered in my actual code that I had two copies of NavigationView––nothing wrong with the TabView code. Removing the one NavigationView not in contentView then caused several functions from working so rebuilt them from scratch. Now have everything working with TabView and NaigationView including the back buttons.

SWIFTUI Button or NavigationLink?

I have a button called "save" that saves the user inputs.
But, I want to make it like, if the user tap on Button "Save", then the screen automatically goes back to the previous view. Can I do that by just adding a code to an action in Button? or do I have to use NavigationLink instead of Button?
Button(action: {
let title = shortcutTitle
currentShortcutTitle = title
UserDefaults.standard.set(title, forKey: "title")
}, label: {
Text("Save")
.padding()
.frame(width: 120, height: 80)
.border(Color.black)
}) //: Button - save
If you're just trying to go back to the previous view and already inside a NavigationView stack, you can use #Environment(\.presentationMode):
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: Screen2()) {
Text("Go to screen 2")
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct Screen2 : View {
#Environment(\.presentationMode) var presentationMode //<-- Here
var body: some View {
Button("Dismiss") {
presentationMode.wrappedValue.dismiss() //<-- Here
}
}
}

Customize navigationBar in Swift UI

I am new in swift ui. I want to put image button on the side of the NavigationBar title.
I want to be able to click the user image and navigate to another view. How?
You need to use navigationBarItems for putting image to navigation bar and you should add NavigationLink to that image. For center the title you need to set navigation bar title's displayMode to .inline or you can use new Toolbar api
struct ContentView: View {
var body: some View {
NavigationView {
Text("Welcome to Stack Overflow")
.navigationBarTitle("Header", displayMode: .inline)
.navigationBarItems(leading: NavigationLink(destination: Text("Destination")) {
Image(systemName: "person.crop.circle.fill")
.font(.title)
})
}
}
}
Screenshot
Another way using toolbar item.
I am adding TapGesture on image icon, and keeping it out of navigation link, as the image is not getting circular inside the NavigationLink in ToolbarItemGroup.
By leveraging isActive property of NavigationLink which monitors onTap state we can determine either we want to push our view or not.
import SwiftUI
struct WeatherView: View {
#State var onTap = false
var borderColor: Color = Color("Black")
var addProjectToolbarItem: some ToolbarContent {
ToolbarItemGroup(placement: .navigationBarLeading) {
NavigationLink(destination:Text("Welcome"), isActive: self.$onTap) {
EmptyView()
}
Image("yourImage")
.resizable()
.frame(width: 32, height: 32)
.clipShape(Circle())
.onTapGesture {
onTap.toggle()
}
}
}
var body: some View {
NavigationView {
VStack {
Text("First view")
}
.toolbar{
addProjectToolbarItem
}
.navigationTitle("Header")
.navigationBarTitleDisplayMode(.inline)
}
}
}

SwiftUI ToolbarItem doesn't present a View from a NavigationLink

I don't know if this is a bug or I am doing something wrong here. I've added a new button on the Navigation bar that would present a new view.
struct MyView: View {
#ObservedObject var viewModel = MyViewModel()
var body: some View {
List(viewModel.data, id: \.name) { data in
NavigationLink(destination: MyDetailView(data: data.name)) {
Text(data.name)
}
}
.listStyle(InsetGroupedListStyle())
.edgesIgnoringSafeArea(.all)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: MyDetailView()) {
Text("New Element")
}
}
}
}
}
This is being tested on the newest iOS 14 beta (beta 6) and Xcode 12 (beta 6). As far as I know a Navigation Link presents fine the new view when on a List but in the toolbar as shown that's not the case. The button on the toolbar it's visible and active but doesn't trigger showing the new view.
I found using an HStack with an empty text as the first element also works, it lets the navigationLink act correctly.
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
HStack {
Text("")
NavigationLink(destination: SettingsView()) {
Image(systemName: "gear")
.font(.title)
}
}
}
NavigationLink should be inside NavigationView. Toolbar is not in NavigationView, put buttons in it.
So assuming you have somewhere in parent
NavigationView {
MyView()
}
here is a solution:
struct MyView: View {
#ObservedObject var viewModel = MyViewModel()
#State private var showNew = false
var body: some View {
List(viewModel.data, id: \.name) { data in
NavigationLink(destination: MyDetailView(data: data.name)) {
Text(data.name)
}
}
.listStyle(InsetGroupedListStyle())
.background(
NavigationLink(destination: MyDetailView(), isActive: $showNew) {
EmptyView()
}
)
.edgesIgnoringSafeArea(.all)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("New Element") {
self.showNew = true
}
}
}
}
}
Using Asperi solution may not work if your navigation link directs to a view with keyboard input.
After navigation link, toolbar in the new view loaded correctly but when providing input with keyboard and dismissing keyboard all toolbar items disappears.
The solution is to place NavigationLink not in View but in navigationBarItems, example:
.navigationBarItems(
leading:
NavigationLink(
destination: Creator(),
isActive: $showCreator,
label: {
Text("")
}))