Navigation stuff in SwiftUI - ios

I'm trying to figure out how to use the navigation bar in SwiftUI
I want to put BarButtonItem and images inside the NavigationBar
I have been able to display the navigation bar and put titles
var body: some View {
NavigationView{
List(0...5) { note in
VStack(alignment: .leading) {
Text("title")
Text("Date")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationBarTitle(Text("Notes"))
}
}

iOS 14
You should use the toolbar modifier:
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") { /* action */ }
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: { /* Actions */ }, label: {
HStack {
Image(systemName: "trash")
Text("Delete")
}
})
.foregroundColor(.red) // You can apply colors and other modifiers too
}
}
Note 1: You can have ANY View there. (not only a Button) and also any modifiers
Note 2: Both codes above and below will generate the same look items but with different approachs
iOS 13 and above (deprecated but still works)
You should use .navigationBarItems() modifier. For example you can add Button or Image like this:
.navigationBarItems(
leading: Button("Cancel") {
// Actions
},
trailing: Button(action: {
// Actions
}, label: { Label("Delete", systemImage: "trash") }
).foregroundColor(.red) // You can apply colors and other modifiers too
)
💡 Pro TIP
Always try to encapsulate each item in a separated struct, so your code will be simplified and very easy to replace with newer technologies. for example, take a look at this sample:
.navigationBarItems(
leading: MyCustomButtonItem(),
trailing: MyCustomButtonItem(text: "foo", image: "Bard")
)

.navigationBarItems() is the function you are looking for. You can specify a leading view, trailing view, or both. Within the view, you can specify horizontal and vertical stacks to add additional buttons.
var body: some View {
NavigationView{
List(0...5) { note in
VStack(alignment: .leading) {
Text("title")
Text("Date")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationBarItems(leading: HStack {
Button(action: {}, label: {Image(systemName: "star.fill")})
Button(action: {}, label: {Text("Edit")})
}, trailing: VStack {
Button(action: {}, label: {Image(systemName: "star.fill")})
Button(action: {}, label: {Text("Edit")})
})
.navigationBarTitle(Text("Notes"))
}
}

SwiftUI 2
In SwiftUI 2 / iOS 14 the navigationBarItems modifier is deprecated.
Instead we should use a toolbar with ToolbarItems.
NavigationView {
List {
// ...
}
.navigationTitle("Notes")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Tap me") {
// action
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Image(systemName: "plus")
}
}
}
You can see the documentation for more ToolbarItemPlacements.

Check the image here
The toolbar() modifier lets us add single or multiple bar button items to the leading and trailing edge of a navigation view, as well as other parts of our view if needed. These might be tappable buttons, but there are no restrictions – you can add any sort of view.
var body: some View {
NavigationView {
ScrollView {
VStack{
}//: VStack
}//: Scroll
.navigationTitle("Settings")
.toolbar(content: {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "xmark")
}
}
})
.padding()
}//: Navigation
}

put this from parentViewController
NavigationLink(destination: NotesListView())

Related

SwiftUI change view from first screen to tabview screen

I want to change views once the user taps 'get started' but due to having navigation view in my first view, it is showing back button on my next screen which I don't want. Please see the images attached below.
Code for the first view is below:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
Text("LifePath")
.font(.system(size: 48, weight: .semibold))
.padding(.bottom)
Spacer()
NavigationLink(destination: ViewChanger()) {
Text("Get Started")
.font(.headline)
.navigationBarBackButtonHidden(true)
}
}
.padding()
}
}
}
back button showing on screen 2
First view
Change the location of your navigationBackButtonHidden modifier so that it actually modifies the view that you're going to (and not the NavigationLink label):
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
Text("LifePath")
.font(.system(size: 48, weight: .semibold))
.padding(.bottom)
Spacer()
NavigationLink(destination: ViewChanger()
.navigationBarBackButtonHidden(true) // <-- Here
) {
Text("Get Started")
.font(.headline)
}
}
.padding()
}
}
}
If you want not only the back button to be gone, but the entire header bar, you can use the .navigationBarHidden(true) modifier.
Also, if you run this on iPad at all, you probably want .navigationViewStyle(StackNavigationViewStyle()) added to the outside of your NavigationView
If you use a NavigationLink (in a NavigationView), the view will be pushed. If you want to replace the view, you can do this with an if statement.
For example, this could be implemented like this:
struct ContentView: View {
#State var showSecondView: Bool = false
var body: some View {
if !showSecondView {
NavigationView {
VStack {
Spacer()
Text("LifePath")
.font(.system(size: 48, weight: .semibold))
.padding(.bottom)
Spacer()
Button(action: { showSecondView = true }) {
Text("Get Started")
.font(.headline)
}
}
.padding()
} else {
TabView {
// ...
}
}
}
}

SwiftUI Xcode 12.3 can't change button size in toolbar

struct ContentView: View {
var body: some View {
NavigationView {
List {
Text("Hi")
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
Text("Title")
.font(.headline)
}
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {}) {
Image(systemName: "person.circle")
.font(.largeTitle)
}
}
}
}
}
}
The .font(.largeTitle) on Image has no effect, only if I use it inside a button.
Is this a bug or am I doing something wrong?
It looks like SwiftUI treats single toolbar items differently (applies their own style, size etc).
A possible workaround is to put a Button in a more complex view, as in: How to change color of ToolbarItem with navigationBarLeading placement in SwiftUI
Adapted to your example it can look like this:
ToolbarItem(placement: .navigationBarLeading) {
HStack {
Button(action: {}) {
Image(systemName: "person.circle")
.font(.largeTitle)
}
Text("")
}
}

Use same `navigationBarTitle` and `navigationBarItems` on two views which are shown based on condition

I have a View which shows Text view to show help text for user to tap on plus icon to add group. Once group is added, it shows List view. To show navigation bar, I need to call navigationBarTitle and navigationBarItems on both Text and List view. Below is my code snippet.
import SwiftUI
struct Home:View {
#EnvironmentObject var dataStore:DataStore
var body: some View {
NavigationView {
if dataStore.groups.isEmpty {
Text("Tap on + icon to add group.")
.font(.caption)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.gray)
.navigationBarTitle(Text("My App Name"), displayMode: .automatic)
.navigationBarItems(
trailing:
NavigationLink(
destination:
CreateGroup(),
label: {
Image(systemName: "plus")
.foregroundColor(Color.blue)
})
)
} else {
List(dataStore.groups) { groupElement in
GroupRow(group: groupElement)
}
.navigationBarTitle(Text("My App Name"), displayMode: .automatic)
.navigationBarItems(
trailing:
NavigationLink(
destination:
CreateGroup(),
label: {
Image(systemName: "plus")
.foregroundColor(Color.blue)
})
)
}
}
}
}
Is there a way to call navigationBarTitle and navigationBarItems only once rather than calling on both Text and List views?
Is there a way to call navigationBarTitle and navigationBarItems only
once rather than calling on both Text and List views?
Yes, you can wrap condition into any container, like Group or xStack:
struct Home:View {
#EnvironmentObject var dataStore:DataStore
var body: some View {
NavigationView {
Group {
if dataStore.groups.isEmpty {
Text("Tap on + icon to add group.")
.font(.caption)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.gray)
} else {
List(dataStore.groups) { groupElement in
GroupRow(group: groupElement)
}
}
}
.navigationBarTitle(Text("My App Name"), displayMode: .automatic)
.navigationBarItems(
trailing:
NavigationLink(
destination:
CreateGroup(),
label: {
Image(systemName: "plus")
.foregroundColor(Color.blue)
})
)
}
}
}

Add Button with Image and Text in ToolbarItem SwiftUI

I want button exactly as in Reminders app in iOS
Below code note working, I also tried using label and system image name.
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button(action: {}, label: {
HStack {
Image(systemName: "plus.circle.fill")
Text("New Reminder")
}
})
}
}
Create a custom label style
struct VerticalLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
VStack {
configuration.icon.font(.headline)
configuration.title.font(.footnote)
}
}
}
Then you can you use the custom style for your label
Button(action: {}, label: {
Label("Home", systemImage: "house")
.labelStyle(VerticalLabelStyle())
})

How to hide the TabBar when navigate with NavigationLink in SwiftUI?

I have a TabView with 2 tabs in it, each tab containing a NavigationView. I need to hide the TabBar when navigating to another view. One solution would be to place the TabView inside of one NavigationView, but I have to set different properties for each NavigationView.
TabView(selection: $selectedTab, content: {
NavigationView {
VStack {
NavigationLink(destination: Text("SecondView Tab1")) {
Text("Click")
}
}
}.tabItem {
Text("ONE")
}.tag(0)
NavigationView {
VStack {
NavigationLink(destination: Text("SecondView Tab2")) {
Text("Click")
}
}
}.tabItem {
Text("TWO")
}.tag(1)
})
P.S. I am using Xcode 11 Beta 5
A little late but it will work, put your NavigationView before the TabView and the tab buttons are going to be hidden when you use a navigation link in your tabbed views.
NavigationView{
TabView{
...
}
}
I have same problem for this;
And I did the following actions to solve this problem:
Use NavigationView Contain a TabView And Hidden the NavigationBar
Make a Custom NavigaitonView like this
In next view Still hidden NavigationBar
// root tab
NavigationView {
TabView {
// some
}
.navigationBarTitle(xxx, displayMode: .inline)
.navigationBarHidden(true)
}
// custom navigation view
#available(iOS 13.0.0, *)
struct MyNavigationView: View {
var body: some View {
HStack {
Spacer()
Text(some)
Spacer()
}
.frame(height: 44)
}
}
// this view
VStack {
MyNavigationView()
Image(some)
.resizable()
.frame(width: 100, height: 100, alignment: .top)
.padding(.top, 30)
Spacer()
HStack {
ClockView()
Spacer()
NavigationLink(
destination: DynamicList(),
label: {
Image(some)
}).navigationBarHidden(true)
}
.padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15))
Spacer()
}
// next view
var body: some View {
VStack {
List {
MyNavigationView()
ForEach(date, id: \.self) { model in
Text(model)
}
}
.navigationBarHidden(true)
.navigationBarTitle(some, displayMode: .inline)
}
}
You can't hide the tab bar as far as I know if you navigation view its listed as a child, your tab bar contains your navigation view.

Resources