Multiple NavigationViews in SwiftUI - How to get rid of multiple toolbar items (i.e the back-button on the left corner)? - swift

I am making an app where the first view the users see is a home screen with buttons that takes them to a second view. One of the second views present the user with a list of items. When the user clicks on one of these items the user comes to a detailed view of the item. When the user comes to the detailed view he is unfortunately presented with two toolbar buttons in the corner as can be seen here:
.
I know that one of the solutions is to only have one navigationview and that solves my problem. But I need to have toolbar items in my listview to be able to add more items, sort the list and have the list searchable which I'm not able to do without navigationView. I Have tried using scrollView and NavigationStack but it comes out blank.
Does anyone have an idea how to work with mulitple views, not getting double up "back buttons" on the toolbar and still have other toolbar items?
View one: (Home Screen):
NavigationView {
ZStack {
VStack {
Text(title)
.font(.custom("Glacial Indifference", size: 34, relativeTo: .headline).weight(.bold))
.multilineTextAlignment(.leading)
.foregroundColor(.white)
.tracking(10)
.padding(8)
.background(
Rectangle()
.fill(.gray)
.frame(width: 1000, height: 150)
.ignoresSafeArea()
.opacity(0.5))
Spacer()
}
VStack {
NavigationLink {
MapView()
} label: {
Buttons(str: "Cheese Map")
}
.padding(.bottom, 200)
}
VStack {
NavigationLink {
TabView()
} label: {
Buttons(str: "Cheese List")
}
.padding(.bottom, 400)
}
Second View (list):
NavigationView {
List {
ForEach(items, id: \.id) { item in
NavigationLink {
ItemView(item: item)
} label: {
ListItem(item: item)
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
showingAddItem = true
} label: {
Image(systemName: "plus")
Text("Add Item")
.font(.footnote)
.italic()
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu("Sort") {
Picker("Filter Options", selection: $selectedSort) {
ForEach(sortOptions, id: \.self) {
value in
Text(value)
.tag(value)
}
}
}
.onChange(of: selectedSort) { _ in
let sortBy = sorts[sortOptions.firstIndex(of: selectedSort)!]
items.sortDescriptors = sortBy.descriptors
}
}
}
.sheet(isPresented: $showingAddItems) {
AddItemsView(items: Items())
}
.navigationTitle("Item List")
.searchable(text: $searchText)
}
}
}
DetailView:
ScrollView {
ZStack {
VStack {
//More code...

Both .toolbar and .searchable find the nearest enclosing NavigationView automatically. You do not need a NavigationView in your list view.
Here's a self-contained demo. It looks like this:
Here's the code:
import SwiftUI
import PlaygroundSupport
struct HomeScreen: View {
var body: some View {
NavigationView {
List {
NavigationLink("Cheese Map") { Text("Map") }
NavigationLink("Cheese List") { ListView() }
}
.navigationTitle("Home Screen")
}
.navigationViewStyle(.stack)
}
}
struct ListView: View {
#State var items = ["Cheddar", "Swiss", "Edam"]
#State var search: String = ""
var filteredItems: [String] {
return items.filter {
search.isEmpty
|| $0.localizedCaseInsensitiveContains(search)
}
}
var body: some View {
List(filteredItems, id: \.self) {
Text($0)
}
.searchable(text: $search)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
items.append("Gouda")
}
} label: {
Label("Add Item", systemImage: "plus")
}
.disabled(items.contains("Gouda"))
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu("Sort") {
Button("Ascending") {
withAnimation {
items.sort()
}
}
Button("Descending") {
withAnimation {
items.sort()
items.reverse()
}
}
}
}
}
.navigationTitle("Cheese List")
}
}
PlaygroundPage.current.setLiveView(HomeScreen())

Related

NavigationStack and TabView - Toolbar and title not showing

I have a problem regarding a TabView that is displayed inside of a NavigationStack. The TabView contains multiple views. Each view has its own navigationBarTitle and toolbar. The problem is that these views toolbar and navigation title are not shown. Just the content that is defined inside the views. I wrote the following code:
struct Home : View {
var body some : View {
NavigationStack {
TabView(selection: $router.currentTab) {
First()
.tag(0)
Second()
.tag(1)
Third()
.tag(2)
Fourth()
.tag(3)
Fifth()
.tag(4)
}
}
}
}
The First() view is defined the following (all other views are structured similar):
struct First: View {
var body: some View {
ZStack {
Color("background").ignoresSafeArea(.all)
ScrollView {
VStack(spacing: 15) {
WhatsNewView()
FavoriteView()
Spacer()
}
}
.refreshable {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
newsService.getArticles()
simpleSuccess()
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
UserButton()
}
ToolbarItem(placement: .navigationBarLeading) {
ToolbarRanking()
}
ToolbarItem(placement: .navigationBarLeading) {
ToolbarCalendar()
}
ToolbarItem(placement: .navigationBarLeading) {
ToolbarSearch()
}
}
.navigationBarTitle("Home")
}
}
Does anyone know how to fix that problem?
The TabView should be your top-level view.
Each tab should then contain its own NavigationStack. e.g.
enum Router {
case screenA, screenB
}
struct ContentView: View {
var body: some View {
TabView() {
FirstScreen()
NavigationStack {
Text("second")
.navigationTitle("second")
}
.tabItem {
Label("second", systemImage: "2.circle")
}
NavigationStack {
Text("third")
.navigationTitle("third")
}
.tabItem {
Label("third", systemImage: "3.circle")
}
NavigationStack {
Text("fourth")
.navigationTitle("fourth")
}
.tabItem {
Label("fourth", systemImage: "4.circle")
}
NavigationStack {
Text("fifth")
.navigationTitle("fifth")
}
.tabItem {
Label("fifth", systemImage: "5.circle")
}
}
}
}
struct FirstScreen: View {
#State private var path: [Router] = []
var body: some View {
NavigationStack(path: $path) {
VStack {
Text("first")
NavigationLink("Screen A", value: Router.screenA)
}
.navigationDestination(for: Router.self) { router in
switch router {
case .screenA:
VStack {
NavigationLink("Screen B", value: Router.screenB)
}
.navigationTitle("Screen A")
case .screenB:
VStack {
Button("Pop to root") {
path = []
}
}
.navigationTitle("Screen B")
}
}
.navigationTitle("first")
}
.tabItem {
Label("first", systemImage: "1.circle")
}
.task {
print("First screen task")
}
.onAppear {
print("First screen appears")
}
}
}

sth wrong with .searchable

I have added searchable to my SwiftUI List.
But the search TextField isn't showing.
Here is my code:
NavigationView {
List(searchResults, id: \.self) { item in
NavigationLink {
LegendDetailView(item: item)
} label: {
HStack {
Text(item.name).padding(1)
Spacer()
Image(systemName: "chevron.right").imageScale(.small)
}
}
}
}.searchable(text: $searchText)
EDIT(2021/5/29):
I thinks there is a piece of important infomation I forgot to say
This view is a popover
I add searchable to my SwiftUI List
No, you added it to the navigation view. Move it up
NavigationView {
List(searchResults, id: \.self) { item in
NavigationLink {
LegendDetailView(item: item)
} label: {
HStack {
Text(item.name).padding(1)
Spacer()
Image(systemName: "chevron.right").imageScale(.small)
}
}
}.searchable(text: $searchText)
}
Text example
struct TestView: View {
#State private var letters = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta"]
#State private var searchText = ""
var body: some View {
let searchResults = searchText.isEmpty ? letters : letters.filter{$0.localizedCaseInsensitiveContains(searchText)}
NavigationView {
List(searchResults, id: \.self) { item in
NavigationLink {
Text(item)
} label: {
HStack {
Text(item).padding(1)
Spacer()
}
}
}.searchable(text: $searchText)
}
}
}

SwiftUI: Native menu items navigation to view

I have like to use the native navigation menu bar in swiftui but somehow I could not get it to navigate to the settings page.
When I click on Settings menu item, it does show "at setting" but it does not navigate to the view.
Any help would be greatly appreciated.
struct ContentView: View {
#StateObject var vm = LoginViewModel()
#State var selection: Int? = 0
var body: some View {
NavigationView {
ZStack {
if !vm.log_status {
Home()
.toolbar{
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button {
print("at home")
} label: {
Label("Home", systemImage: "house.fill")
}
Button {
print("at setting")
self.selection = 1
} label: {
NavigationLink(destination: Text("Settings Page"), tag:1, selection: $selection) { Label("Settings", systemImage: "gearshape.fill")}
}
} label: {
Label(title: { Text("Menu")}, icon: {Image(systemName: "filemenu.and.selection").foregroundColor(Color.black).font(.system(size: 24))})
}
}
}
} else {
Login()
}
}
.navigationTitle("App")
}
}
}
It is not necessary to have the NavigationLink inside a button itself. You can move it within the ZStack and get rid of any label you defined. By doing so, when you tap on the button where "at setting" is printed, you still change the selection value which in return triggers the navigation call. I've made a few changes to your code (Read the comments):
NavigationView {
ZStack {
NavigationLink(destination: Text("Settings Page"), tag:1, selection: $selection) {} // Move the NavigationLink to the ZStack and get rid of any labels set.
if !vm.log_status {
Text("Home")
.toolbar{
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button {
print("at home")
} label: {
Label("Home", systemImage: "house.fill")
}
Button {
print("at setting")
self.selection = 1
} label: {
Label("Settings", systemImage: "gearshape.fill") // Keep ONLY the label of the Previous NavigationLink
}
} label: {
Label(title: { Text("Menu")}, icon: {Image(systemName: "filemenu.and.selection").foregroundColor(Color.black).font(.system(size: 24))})
}
}
}
} else {
Text("Login")
}
}
.navigationTitle("App")
}
The result:

SwiftUI navigationBarItems disappear in TabView

I have a view that has navigation bar items and I embed that view in a TabView. But when doing that, the bar items no longer appear. If I call the view outside of a TabView everything works as expected.
Below a small sample project to illustrate my issue, note that the TabView is not called on the initial ContentView but later down:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink(destination: WarehouseOrderTabView()){
Text("Click me")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct WarehouseOrderTabView: View {
var body: some View {
TabView{
TabView1().navigationBarTitle("Dashboard")
.tabItem {
Image(systemName: "gauge")
Text("Dashboard")
}
TabView2().navigationBarTitle("Orders")
.tabItem {
Image(systemName: "list.dash")
Text("Orders")
}
}
}
}
struct TabView1: View {
var body: some View {
Text("TabView 1")
//I would expect to see those bar items when displaying tab 1
.navigationBarItems(trailing: (
HStack{
Button(action: {
}, label: {
Image(systemName: "arrow.clockwise")
.font(.title)
})
.padding(.init(top: 0, leading: 0, bottom: 0, trailing: 20))
Button(action: {
}, label: {
Image(systemName: "slider.horizontal.3")
.font(.title)
})
}
))
}
}
struct TabView2: View {
var body: some View {
Text("TabView 2")
}
}
What am I missing here?
A NavigationView can be embedded in a TabView and not vice-versa.
TabView contains different tabItem() (at most 5) that can contain your views.
This is how you can use it.
TabView1.swift
struct TabView1: View {
var body: some View {
NavigationView {
Text("TabView 1")
.navigationBarTitle("Dashboard")
.navigationBarItems(trailing:
HStack {
Button(action: {
// more code here
}) {
Image(systemName: "arrow.clockwise")
.font(.title)
}
Button(action: {
// more code here
}) {
Image(systemName: "slider.horizontal.3")
.font(.title)
}
}
)
}
}
}
TabView2.swift
struct TabView2: View {
var body: some View {
NavigationView {
NavigationLink(destination: YourNewView()) {
Text("TabView 1")
}
.navigationBarTitle("Orders")
}
}
}
ContentView.Swift
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
TabView1()
.tabItem {
Image(systemName: "gauge")
Text("Dashboard")
}
TabView2()
.tabItem {
Image(systemName: "list.dash")
Text("Orders")
}
}
}
}
Hope it helps :)

How to create an UItoolbar in SwiftUI with Xcode 11.2

I don't know how to create an UIToolbar at the bottom of the screen in SwiftUI.
There is no controller in SwiftUI but you can add like this.
var body: some View {
NavigationView {
VStack {
List(model.items) { item in
ItemViewRow(item: item)
}
HStack {
Button(action: {
}) {
Image(systemName: "someimage")
}
Spacer()
Button(action: {
}) {
Image(systemName: "someimage")
}
}.padding()
}
.navigationBarTitle(Text("Items"))
}
}