I have a problem. I have empty space on the top of my views, and I think that the problem is the Navigation View. View Image
I figured it out to make it work and hide that empty space with this line of code, but if I'm using this approach, my toolbar items dissapears too, and I do not want this.
.navigationBarHidden(true)
I'll share my code below. Thanks !
TabView{
NavigationView{
VStack {
MeniuriView()
NavigationLink(isActive: $optionsActive) {
WaitingOrderView()
.environmentObject(syncViewModel)
} label: {
EmptyView()
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ToolbarButtons(numberOfProducts: menus.count) {
optionsActive = true
}
}
ToolbarItem(placement: .navigationBarLeading) {
Text(Texts.mainViewText1)
.font(.system(size: 24))
.fontWeight(.bold)
.padding()
}
}
}
.tabItem {
Text(Texts.mainViewText2)
Image(systemName: "fork.knife")
}
}
struct MeniuriView: View {
#EnvironmentObject var syncViewModel : SyncViewModel
var body: some View {
List {
ForEach(syncViewModel.menuType) { type in
SectionView(menuType: type)
}
}
.listStyle(PlainListStyle())
}
}
The space is reserved for the (large) NavigationTitle. You can use
.navigationBarTitleDisplayMode(.inline)
on the NavigationView to make it small. And if its empty, it won't show.
Related
I want to have a sticky toolbar that has multiple different filtering options above a List in SwiftUI. Is there a way to just place a full custom view inside the .toolbar property of a List?
With the below code, things look very unexpected
var body: some View {
NavigationView {
List() {
Text("List item")
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
VStack {
Toggle(isOn: $viewModel.isPound) {
Text("test")
}
.toggleStyle(.switch)
Text("A slider to control data")
Text("Another filtering option")
Text("Some random piece of information")
}
}
}
}
}
The NavigationBar is a fixed height for iOS, and is really designed for buttons. You can see the problem (and the space you have to work with) if you add a background colour to it.
struct ContentView: View {
#State private var isPound = false
var body: some View {
NavigationView {
List() {
Text("List item")
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
VStack {
Toggle(isOn: $isPound) {
Text("test")
}
.toggleStyle(.switch)
Text("A slider to control data")
Text("Another filtering option")
Text("Some random piece of information")
}
.border(.red)
}
}
.toolbarBackground(.visible, for: .navigationBar)
.toolbarBackground(.red, for: .navigationBar)
}
}
}
I have a button that opens up a Profile & Settings view in a sheet that has additional navigation views in it.
I am aware how to dismiss the sheet, however this method seems to not work with additional navigation views, as when I'm deeper into the navigation and I tap "Done" to dismiss the sheet, it only returns me back to the previous navigation view until I go back to the main Profile & Settings view.
The view with the button:
import SwiftUI
struct TodayView: View {
#State private var showSheet = false
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading) {
TodayTabDateComponent()
.padding(.top, -10)
ForEach(0 ..< 32) { item in
VStack(alignment: .leading) {
Text("Title")
Text("Description")
}
.padding(.vertical)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
}
.navigationTitle("Today")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showSheet = true
}, label: {
Image(systemName: "person.circle.fill")
.foregroundColor(.primary)
})
.sheet(isPresented: $showSheet) {
ProfileAndSettingsView()
}
}
}
}
}
}
struct TodayView_Previews: PreviewProvider {
static var previews: some View {
TodayView()
}
}
The Profile & Settings view:
import SwiftUI
struct ProfileAndSettingsView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
VStack {
List {
Section {
NavigationLink {
UserProfileView()
} label: {
HStack(alignment: .center) {
Image("avatar")
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.frame(width: 60, height: 60)
VStack(alignment: .leading) {
Text("Name Surname")
.font(.title2)
.fontWeight(.bold)
Text("Profile Settings, Feed Preferences\n& Linked Accounts")
.font(.caption)
}
}
}
.padding(.vertical, 6)
} }
.listStyle(.insetGrouped)
.navigationTitle("Profile & Settings")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Text("Done")
}
}
}
}
}
}
}
struct ProfileAndSettingsView_Previews: PreviewProvider {
static var previews: some View {
ProfileAndSettingsView()
}
}
I have looked into the issue but couldn't find any working solutions.
Is your issue here that you're applying the .sheet to the Button inside the Toolbar? I think you need to apply it to the NavigationView itself?
import SwiftUI
struct TodayView: View {
#State private var showSheet = false
var body: some View {
NavigationView {
....
}
.navigationTitle("Today")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showSheet = true
}, label: {
Image(systemName: "person.circle.fill")
.foregroundColor(.primary)
})
}
}
.sheet(isPresented: $showSheet) {
ProfileAndSettingsView()
}
}
}
}
If you're targeting iOS15 or higher don't use presentationMode use #Environment(\.isPresented) private var isPresented instead this will perform the action that you want.
presentationMode was deprecated and replaced by isPresented and dismiss
I believe that presentationMode performs a similar action as dismiss does which according to Apple Docs (on the dismiss)
If you do this, the sheet fails to dismiss because the action applies to the environment where you declared it, which is that of the detail view, rather than the sheet. In fact, if you’ve presented the detail view in a NavigationView, the dismissal pops the detail view the navigation stack.
The dismiss action has no effect on a view that isn’t currently presented. If you need to query whether SwiftUI is currently presenting a view, read the isPresented environment value.
If you're targeting a lower iOS version you can create your own key like so
struct SheetOpen: EnvironmentKey {
static var defaultValue: Binding<Bool> = .constant(false)
}
extension EnvironmentValues {
var sheetOpen: Binding<Bool> {
get { self[SheetOpen.self] }
set { self[SheetOpen.self] = newValue }
}
}
Where you have your sheet defined you do this
.sheet(isPresented: $showSheet) {
ProfileAndSettingsView()
.environment(\.sheetOpen, $showSheet)
}
Then you can use it like any other environment variable
#Environment(\.sheetOpen) var sheetOpen
To dismiss it you simply do this sheetOpen.wrappedValue.toggle()
I have the following in problem.
If I try to give a view a color (Color.red for example)
I get the following output:
I can just add .edgesignoressafearea(.top) and the top also gets red. But when I want to add an clickable item, the user won't be able to click it since there still is this invisible bar on the top of the screen. Does Anyone know what my problem is? The problem is in all the tabable views(Timeline, Favorits, Discover, Account). So It must be in either the first code or in tabview (second code) that I send in this post.
When the user clicks on the app first they get this view sending the user to the signin view or the app itself:
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: tabView().navigationBarHidden(true), isActive: $tabview, label: { EmptyView() })
NavigationLink(destination: loginView().navigationBarHidden(true), isActive: $login, label: { EmptyView() })
if tabview == false && login == false {
Text("loading")
.onAppear(perform: checklogin)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Then the apps sends them to tabview:
var body: some View {
TabView(selection: $selection) {
Timeline()
.tabItem {
Label("timeline", systemImage: "house")
}
.tag(0)
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
.tag(1)
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
.tag(2)
Account()
.tabItem {
Label("account", systemImage: "person")
}
.tag(3)
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
The problem happens on all of this views.
This is the view where I made the screenshot of:
var body: some View {
ZStack {
Color.red
Text("Hello fav!")
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
add .edgesIgnoringSafeArea(.top) and remove your navigation bar if you don't want to be able to get back to your login view anytime.
That's the full code with a login page, now it depends what you want on that login page :
import SwiftUI
struct ContentView: View {
#State var showLoginView: Bool = false
var body: some View {
VStack {
if showLoginView {
MainView()
} else {
Button("Login") {
self.showLoginView = true
}
}
}
}
}
struct MainView: View {
var body: some View {
TabView {
TimeLine()
.tabItem {
Label("timeline", systemImage: "house")
}
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
Account()
.tabItem {
Label("account", systemImage: "person")
}
}
}
}
struct TimeLine: View {
var body: some View {
Color.blue
.edgesIgnoringSafeArea(.top)
}
}
struct Favorits: View {
var body: some View {
ZStack {
Color.red
.edgesIgnoringSafeArea(.top)
Text("Hello fav!")
}
}
}
struct Discover: View {
var body: some View {
Color.yellow
.edgesIgnoringSafeArea(.top)
}
}
struct Account: View {
var body: some View {
Color.purple
.edgesIgnoringSafeArea(.top)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You need to set the navigation Title to "" because your View is embedded in NavigationView
Create a ViewModifier like this and apply it to your VStack
struct HiddenNavBarModifier: ViewModifier {
func body(content: Content) -> some View {
content
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}
What I'm gonna do is..
If I click a menu, then it goes to a detailed menu-description page.
but It seems to something wrong. the code is the below.
[contentview.swift]
var body: some View {
VStack(alignment:.leading){
List{
Text("Menu")
.font(.title)
.fontWeight(.heavy)
ForEach(menu){ section in
Section(header: Text(section.name) , content: {
ForEach(section.items.indices)
{
index in ItemRow(item:section.items[index])
}
})
}
}.listStyle(GroupedListStyle())
}
}
[ItemRow.swift]
struct ItemRow: View {
let item: MenuItem
var body: some View {
VStack{
//NavigationView{
NavigationLink(destination: ItemDetail(item:item)) {
HStack{
Image(item.thumbnailImage)
VStack(alignment:.leading){
Text(item.name)
.font(.headline)
Text("$\(item.price)")
}
}
}
//}
}
}
}
I intentionally commented out the 'NaviagationView'.
because If I activate that comment relating to NavigationNiew..
See? It seems weird. I still wonder what i did wrong..
Before adding the navigation code, the menu was shown on the Preview well.
Could you give me some tips?
===============================
My Problem has been solved!
[ContentView.swift]
struct ContentView: View {
var body: some View {
ItemList()
}
}
[ItemList.swift]
struct ItemList: View {
var body: some View {
NavigationView{
VStack(alignment:.leading){
List{
Text("Menu")
.font(.title)
.fontWeight(.heavy)
ForEach(menu){ section in
Section(header: Text(section.name) , content: {
ForEach(section.items.indices)
{
index in
//ItemRow(item:section.items[index])
NavigationLink(destination: ItemDetail(item:section.items[index])) {
HStack{
Image(section.items[index].thumbnailImage)
VStack(alignment:.leading){
Text(section.items[index].name)
.font(.headline)
Text("$\(section.items[index].price)")
}
}
}
}
})
}
}.listStyle(GroupedListStyle())
}
}
}
}
There is usually only one NavigationView in an app. It is usually the outer most View.
In this case put it above the VStack in the ContentView.
One of the most common exceptions are sheets.
The problem: When I lunch the app, the first tabview doesn't display the name below. I have to go to another view and come back for it to show. Why?
struct MotherView : View {
var body: some View {
VStack {
if viewRouter.currentPage == "onboardingView" {
OnboardingView()
} else if viewRouter.currentPage == "homeView" {
TabView {
HomeView()
.tabItem {
Image(systemName: "house")
Text("Menu")
}
SettingsView()
.tabItem {
Image(systemName: "slider.horizontal.3")
Text("Order")
}
}
}
}
}
}
We can just guess, because you didn't show us the code of your HomeView.
Nevertheless I guess in your HomeView you have this line .navigationBarTitle("", displayMode: .inline).
Remove it and it will work as expected.