Why is the navigation bar so tall - swift

Im running Xcode 14 beta 6 and this page is a link from my home page and the nav bar is stacking like there is two.
If you don't understand here's some images
and here's my code:
import SwiftUI
struct ProductsLink: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "arrow.backward.circle")
.aspectRatio(contentMode: .fit)
.foregroundColor(Color("LaunchText"))
}
}
}
var body: some View {
Products(text: "All Products")
.navigationBarItems(leading: btnBack)
.navigationBarBackButtonHidden()
}
}
struct ProductsLink_Previews: PreviewProvider {
static var previews: some View {
ProductsLink()
}
}
contentview:
import SwiftUI
struct ContentView: View {
#State private var selection = 1
init() {
UITabBar.appearance().backgroundColor = .systemBackground
}
var body: some View {
TabView(selection: $selection {
Home()
.tabItem { Label("Home", systemImage: "house.fill") }.tag(1)
About()
.tabItem { Label("About", systemImage: "person.crop.circle") }.tag(2)
Products()
.tabItem { Label("Products", systemImage: "bag.fill") }.tag(3)
Gallery()
.tabItem { Label("Gallery", systemImage: "photo.on.rectangle") }.tag(4)
Contact()
.tabItem { Label("Contact", systemImage: "phone.bubble.left.fill") }.tag(5)
}
.accentColor(Color("Green Power Logo Colour"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
products:
struct Products: View {
var text = "Products"
var body: some View {
NavigationView {
ScrollView {
AllProductCards
NavigationLink(destination: ContactForm()) {
HStack {
Text("Intrested. Contact Form \(Image(systemName: "arrow.forward.circle"))")
.foregroundColor(.white)
} .frame(width: 250 ,height: 50)
.background(Color("Dark Green"))
.cornerRadius(7)
}
Footer()
}
.navigationTitle(text)
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct Products_Previews: PreviewProvider {
static var previews: some View {
Products()
}
}

First one thing, in the ContentView, you are referencing the Products view. If look into the Products view, there is an AllProductCards reference to (I guess) another view. I don't know what's in the view. My assumption would be, it is a List/VStack with NavigationLinks to the ProductsLinks. The ProductsLinks itself are referencing to Products. Is that correct, or what is AllProductsLinks doing? If my assumption is correct, you are looping the products. Since the NavigationView() is part of the Products, that causes a nested NavigationView() into another NavigationView()
I'd think about the looping situation and change that.
For now and here it is "fixed" if you pull out the NavigationView(). You would have to moved the NavigationView() from the Products view into the ContentView like below. So even if the Products view is looped, you have just one NavigationView().
Again, I implemented the AllProductCards according to my assumption.
The updated ContentView (Products nested into NavigationView()):
import SwiftUI
struct ContentView: View {
#State private var selection = 1
init() {
UITabBar.appearance().backgroundColor = .systemBackground
}
var body: some View {
TabView(selection: $selection {
Home()
.tabItem { Label("Home", systemImage: "house.fill") }.tag(1)
About()
.tabItem { Label("About", systemImage: "person.crop.circle") }.tag(2)
NavigationView {
Products()
}
.tabItem { Label("Products", systemImage: "bag.fill") }.tag(3)
Gallery()
.tabItem { Label("Gallery", systemImage: "photo.on.rectangle") }.tag(4)
Contact()
.tabItem { Label("Contact", systemImage: "phone.bubble.left.fill") }.tag(5)
}
.accentColor(Color("Green Power Logo Colour"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The updated Products view (removed NavigationView()):
struct Products: View {
var text = "Products"
var body: some View {
ScrollView {
AllProductCards()
NavigationLink(destination: ContactForm()) {
HStack {
Text("Intrested. Contact Form \(Image(systemName: "arrow.forward.circle"))")
.foregroundColor(.white)
} .frame(width: 250 ,height: 50)
.background(Color("Dark Green"))
.cornerRadius(7)
}
Footer()
}
.navigationTitle(text)
}
}

Related

MacOS SwiftUI strange behavior opening sidebar

As can be seen from the image when opening the side menu to a strange behavior it seems.
Can you tell me how to solve the problem?
ContentView
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, world!")
}.navigationTitle("Explore")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
projectSchemaApp
import SwiftUI
#main
struct projectSchemaApp: App {
var body: some Scene {
WindowGroup {
SidebarView()
.frame(minWidth: 600, minHeight: 600)
}
}
}
SidebarView
import SwiftUI
struct SidebarView: View {
var body: some View {
NavigationView {
List {
Text("Simboli SF")
Group{
NavigationLink(destination: ContentView()) {
Label("Tutti", systemImage: "message")
}
NavigationLink(destination: ContentView()) {
Label("Novità", systemImage: "cloud.sun")
}
NavigationLink(destination: ContentView()) {
Label("Multicolore", systemImage: "bolt.car")
}
NavigationLink(destination: ContentView()) {
Label("Comunicazione", systemImage: "pills")
}
NavigationLink(destination: ContentView()) {
Label("Meteo", systemImage: "ticket")
}
NavigationLink(destination: ContentView()) {
Label("Oggetti e strumenti", systemImage: "function")
}
}
Spacer()
Text("Libreria")
NavigationLink(destination: ContentView()) {
Label("Simboli personalizzati", systemImage: "option")
}
}
.listStyle(SidebarListStyle())
.navigationTitle("Explore")
.frame(minWidth: 10, maxWidth: 200)
.toolbar{
ToolbarItem(placement: .primaryAction){
Button(action: toggleSidebar, label: {
Image(systemName: "sidebar.left")
})
}
}
ContentView()
}
}
}
func toggleSidebar() {
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
struct SidebarView_Previews: PreviewProvider {
static var previews: some View {
SidebarView()
}
}

Why would an init cause a NavigationLink to fail in Swiftui?

I have two pieces of test code to check that things worked out. Basically I wanted to navigate from one view to a specific tag on a TabView page. Everything worked out until I put an init in the destination file. Is there a way to do this so adding an init won't break things? Here are the test files:
import SwiftUI
struct ContentView: View {
#State var selectedView = ""
var body: some View {
NavigationView {
NavigationLink("go", destination: PageTwo(selectedView: "three"))
.foregroundColor(Color.black)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and
import SwiftUI
struct PageTwo: View {
#State var selectedView = ""
// Adding this breaks the link between the files -> init(){}
var body: some View {
TabView(selection: $selectedView) {
Button("Show Second View") {
selectedView = "two"
}
.padding()
.tabItem {
Label("First", systemImage: "1.circle")
}
.tag("one")
Button("Show First View") {
selectedView = "one"
}
.padding()
.tabItem {
Label("Second", systemImage: "2.circle")
}
.tag("two")
Button("Show First View") {
selectedView = "one"
}
.padding()
.tabItem {
Label("Third", systemImage: "3.circle")
}
.tag("three")
}
}
}
struct PageTwo_Previews: PreviewProvider {
static var previews: some View {
PageTwo()
}
}
Thanks for any help.

Swiftui invisible bar on the top of the screen

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)
}
}

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 :)

Pop to root view using Tab Bar in SwiftUI

Is there any way to pop to root view by tapping the Tab Bar like most iOS apps, in SwiftUI?
Here's an example of the expected behavior.
I've tried to programmatically pop views using simultaneousGesture as follow:
import SwiftUI
struct TabbedView: View {
#State var selection = 0
#Environment(\.presentationMode) var presentationMode
var body: some View {
TabView(selection: $selection) {
RootView()
.tabItem {
Image(systemName: "house")
.simultaneousGesture(
TapGesture().onEnded {
self.presentationMode.wrappedValue.dismiss()
print("View popped")
}
)
}.tag(0)
Text("")
.tabItem {
Image(systemName: "line.horizontal.3")
}.tag(1)
}
}
}
struct RootView: View {
var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Go to second view")
}
}
}
}
struct SecondView: View {
var body: some View {
Text("Tapping the house icon should pop back to root view")
}
}
But seems like those gestures were ignored.
Any suggestions or solutions are greatly appreciated
We can use tab bar selection binding to get the selected index. On this binding we can check if the tab is already selected then pop to root for navigation on selection.
struct ContentView: View {
#State var showingDetail = false
#State var selectedIndex:Int = 0
var selectionBinding: Binding<Int> { Binding(
get: {
self.selectedIndex
},
set: {
if $0 == self.selectedIndex && $0 == 0 && showingDetail {
print("Pop to root view for first tab!!")
showingDetail = false
}
self.selectedIndex = $0
}
)}
var body: some View {
TabView(selection:selectionBinding) {
NavigationView {
VStack {
Text("First View")
NavigationLink(destination: DetailView(), isActive: $showingDetail) {
Text("Go to detail")
}
}
}
.tabItem { Text("First") }.tag(0)
Text("Second View")
.tabItem { Text("Second") }.tag(1)
}
}
}
struct DetailView: View {
var body: some View {
Text("Detail")
}
}
I messed around with this for a while and this works great. I combined answers from all over and added some stuff of my own. I'm a beginner at Swift so feel free to make improvements.
Here's a demo.
This view has the NavigationView.
import SwiftUI
struct AuthenticatedView: View {
#StateObject var tabState = TabState()
var body: some View {
TabView(selection: $tabState.selectedTab) {
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[0]) {
Text("GOTO TestView #1")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.first
}).tabItem {
Label("First", systemImage: "list.dash")
}.tag(TabState.Tab.first)
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[1]) {
Text("GOTO TestView #2")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}.navigationTitle("")
.navigationBarTitleDisplayMode(.inline).navigationBarTitle(Text(""), displayMode: .inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.second
}).tabItem {
Label("Second", systemImage: "square.and.pencil")
}.tag(TabState.Tab.second)
}
.onReceive(tabState.$selectedTab) { selection in
if selection == tabState.lastSelectedTab {
tabState.showTabRoots[selection.rawValue] = false
}
}
}
}
struct AuthenticatedView_Previews: PreviewProvider {
static var previews: some View {
AuthenticatedView()
}
}
class TabState: ObservableObject {
enum Tab: Int, CaseIterable {
case first = 0
case second = 1
}
#Published var selectedTab: Tab = .first
#Published var lastSelectedTab: Tab = .first
#Published var showTabRoots = Tab.allCases.map { _ in
false
}
}
This is my child view
import SwiftUI
struct TestView: View {
let titleNum: Int
let title: String
init(titleNum: Int) {
self.titleNum = titleNum
self.title = "TestView #\(titleNum)"
}
var body: some View {
VStack {
Text(title)
NavigationLink(destination: TestView(titleNum: titleNum + 1)) {
Text("Goto View #\(titleNum + 1)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
NavigationLink(destination: TestView(titleNum: titleNum + 100)) {
Text("Goto View #\(titleNum + 100)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView(titleNum: 0)
}
}
You can achieve this by having the TabView within a NavigationView like so:
struct ContentView: View {
#State var selection = 0
var body: some View {
NavigationView {
TabView(selection: $selection) {
FirstTabView()
.tabItem {
Label("Home", systemImage: "house")
}
.tag(0)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct FirstTabView: View {
var body: some View {
NavigationLink("SecondView Link", destination: SecondView())
}
}
struct SecondView: View {
var body: some View {
Text("Second View")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
}
}
}