How to permanently hide NavigationView nav bar in SwiftUI - ios

I believe the recommended way to hide the navigation bar in SwiftUI is as follows (placed on the child of the NavigationView) however when a button is tapped on the view, the view updates itself & the nav bar reappears even though I'm using a constant value of true to hide the bar
.navigationBarTitle("")
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
How do I get the NavigationView's bar to stay hidden on this view?
EDIT
Here is the code for NavigationView, when a gesture/tap happens in a nested subview inside props.selectedView the bar appears again
NavigationView {
GeometryReader { geometry in
VStack(spacing: 0) {
ZStack {
VStack {
props.selectedView
Spacer()
.frame(height: searchHeaderHeight)
}
Search()
}
TabBar()
.frame(width: geometry.size.width, height: 60)
.padding(.bottom, geometry.safeAreaInsets.bottom)
.background(Color(.systemGroupedBackground))
}
.frame(width: geometry.size.width)
.edgesIgnoringSafeArea(.bottom)
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}.navigationViewStyle(StackNavigationViewStyle())

Related

SwiftUI - How to programatically scroll to top and show a large navigation title?

Recently added to iOS 14 ScrollViewReader added a way to programmatically scroll to a view in SwiftUI.
Q: Is there a way to scroll past the topmost element in ScrollView and reveal a large navigation title?
Expected effect:
Large navigation title after scrolling to top
Outcome:
Inline navigation title after scrolling to top
Code snippet for reference:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollViewReader { proxy in
ScrollView {
Rectangle()
.fill(.yellow)
.aspectRatio(contentMode: .fit)
.id("scrollID")
Rectangle()
.fill(.blue)
.aspectRatio(contentMode: .fit)
Rectangle()
.fill(.green)
.aspectRatio(contentMode: .fit)
Rectangle()
.fill(.red)
.aspectRatio(contentMode: .fit)
Button("Scroll to top") {
withAnimation {
proxy.scrollTo("scrollID")
}
}
}
.navigationTitle("Navigation")
}
}
}
}
This code scrolls to a top rectangle on the button tap but it doesn't reveal the default large navigation title. How could I achieve that?
(I haven't tested it!) Try:
declare state var to toggle navigation display mode
add .navigationBarTitle("Home", displayMode: showLargeDisplayMode ? .large : .inline)
toggle showLargeDisplayMode after proxy.scrollTo("scrollID")

SwiftUI - TabView initial tabItem not displaying text

Summary of the problem
I have HomeView which contains the TabView (which is inside a NavigationView, see the code below). If I am to load the HomeView from another view (LoginView) it loads as expected and everything works. If I try to load the HomeView directly like this (code is in my ContentView):
if authService.isLoggedIn {
HomeView()
} else {
LoginView()
}
it again loads the HomeView with the tabs at the bottom but my first tab is missing text and only displays its image. Strangely if I switch to a tab (i.e. click Account) the text on the first tab appears again.
Here is the code for my TabView:
NavigationView {
TabView(selection: $selected) {
PlayView()
.tabItem {
Image(systemName: "play.circle.fill")
.font(.system(size: 24))
Text("Play")
}
.navigationBarHidden(true)
.navigationBarTitle("")
.tag(1)
AccountView()
.tabItem {
Image(systemName: "person.circle.fill")
.font(.system(size: 24))
Text("Account")
}
.tag(3)
NotificationsView()
.tabItem {
Image(systemName: "bell.fill")
.font(.system(size: 24))
Text("Notifications")
}
.tag(4)
}
.accentColor(Color(K.Colors.Secondary))
.navigationBarTitle("")
.navigationBarHidden(true)
}
Expected Result
Actual Result
Note: Notice how the icon is displayed at lower level than other icons, so the text is actually not displaying at all
What I Tried So Far
I did try only a few things, because the TabView is not very well documented on Apple's official documentation.
I tried moving the NavigationView both down and up the view hierarchy
Setting another tab for initial selection
Switching places of Image() and Text() inside the .tabItem section
Searching for a similar problem through the internet
I have found a solution! Turns out what was showing at the bottom was that navigation bar title, which I was setting to an empty string. So I changed my navigation bar titles on every View of the TabView elements. The code now looks like this:
NavigationView {
TabView(selection: $selected) {
PlayView()
.tabItem {
Image(systemName: "play.circle.fill")
.font(.system(size: 24))
Text("Play")
}
.navigationBarHidden(true)
.navigationBarTitle("Play")
.tag(1)
AccountView()
.tabItem {
Image(systemName: "person.circle.fill")
.font(.system(size: 24))
Text("Account")
}
.navigationBarHidden(true)
.navigationBarTitle("Account")
.tag(3)
NotificationsView()
.tabItem {
Image(systemName: "bell.fill")
.font(.system(size: 24))
Text("Notifications")
}
.navigationBarHidden(true)
.navigationBarTitle("Notifications")
.tag(4)
}
.accentColor(Color(K.Colors.Secondary))
.navigationBarTitle("")
.navigationBarHidden(true)
}
and It works as expected.

Hide NavigationView bar in landscape mode and delay in hiding in SwiftUI

I'm still learning to SwiftUI and am playing with the NavigationView and I am able to hide the top navigation bar in portrait mode but have two issues.
1.) In landscape mode, there's a top bar that still sits on top of the view. Pulling it down brings down the notification center/settings
2.) After tapping the button in the view, it moves to the DetailView, but the top navigation bar is loaded briefly and then hides. How to stop the navigation from loading.
GIF of button to next view - the flow from tapping button to moving to next view to changing to landscape mode
Any feedback or approach is appreciated. Thanks!
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Hello World")
.fontWeight(.bold)
.font(.title)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.blue)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
)
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
//.edgesIgnoringSafeArea([.top, .bottom])
//.navigationViewStyle(StackNavigationViewStyle())
//.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
//.statusBar(hidden: true)
}
}
I was also seeing the navigation bar shown briefly in the destination view before it disappeared. To fix that, I added the navigation bar properties to the destination view argument inside the NavigationLink call. For your example, it would look like this:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
) { // The rest of your code...

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.

How to put a logo in NavigationView in SwiftUI?

I am trying to use a logo image instead of a NavigationView title at the top section of the app. Couldn't find any documentation of using images inside a NavigationView.
iOS 14+
Starting from iOS 14 you can create a ToolbarItem with the principal placement:
struct ContentView: View {
var body: some View {
NavigationView {
Text("Test")
.toolbar {
ToolbarItem(placement: .principal) {
Image(systemName: "ellipsis.circle")
}
}
}
}
}
See the ToolbarItemPlacement documentation for more placements.
NavigationView.navigationBarTitle() can only take a Text() argument right now. You could instead use .navigationBarItems() to set an Image as either the trailing or leading argument, but this is the SwiftUI equivalent of UINavigationItem.leftBarButtonItem[s] and UINavigationItem.rightBarButtonItem[s], which means that you're restricted to navigation bar button dimensions. But if you're ok with that, you may want to set a blank title so that you can specify a standard-height navigation bar.
Hard-Coded Positioning
If you can stand to live with yourself, you can fake a centered nav bar item by hard-coding padding around the image, like
.padding(.trailing, 125),
(Note that I deliberately positioned it off-center so that you can see that it's hard-coded.)
Slightly Less Hard-Coded Positioning
Even better would be to wrap the whole thing in a GeometryReader { geometry in ... } block to use the screen dimensions to calculate precise positioning, if you know the exact width of the image you're using:
GeometryReader { geometry in
NavigationView {
...
}
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(trailing:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(.trailing, (geometry.size.width / 2.0) + -30), // image width = 60
destination: ProfileHost()
)
)
If you don't want to hack it, here's what you can do:
Standard nav bar height, left button item
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(leading:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(),
destination: ProfileHost()
)
)
Standard nav bar height, right button item
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(trailing:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(),
destination: ProfileHost()
)
)
Expanded nav bar height, no title, left button item
.navigationBarItems(leading:
PresentationButton(
Image(systemName: "person.crop.circle")
.imageScale(.large)
.padding(),
destination: ProfileHost()
)
)
Use this:
NavigationView {
Text("Hello, SwiftUI!")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
HStack {
Image(systemName: "sun.min.fill")
Text("Title").font(.headline)
}
}
}
}
Credit: https://sarunw.com/posts/custom-navigation-bar-title-view-in-swiftui/
With SwiftUIX, you can use navigationBarTitleView(View):
NavigationView() {
NavigationLink(destination:YourView().navigationBarTitleView(Image(systemName: "message.fill")))
}
I don't want to claim 100% accuracy whether title image positioned at center but visually it looks center to me. Do your judgment and adjust padding :)
Here is code:
.navigationBarTitle(
Text("")
, displayMode: .inline)
.navigationBarItems(leading:
HStack {
Button(action: {
}) {
Image(systemName: "arrow.left")
}.foregroundColor(Color.oceanWhite)
Image("oceanview-logo")
.resizable()
.foregroundColor(.white)
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 40, alignment: .center)
.padding(UIScreen.main.bounds.size.width/4+30)
}
,trailing:
HStack {
Button(action: {
}) {
Image(systemName: "magnifyingglass")
}.foregroundColor(Color.oceanWhite)
}
)
To extend on NRitH's answer, putting your logo in a different component (to borrow a React way of putting it) may help anyone looking to understand the concepts.
The actual Image can be wrapped in any container view such as a VStack, etc. An example of setting up a struct as a component to be used in our navigation items could be something like the following:
struct NavLogo: View {
var body: some View {
VStack {
Image("app-logo")
.resizable()
.aspectRatio(2, contentMode: .fit)
.imageScale(.large)
}
.frame(width: 200)
.background(Color.clear)
}
}
When the aspect ratio is set, only the width needs to be set on the frame on the container view. We could also set a property in the NavLogo to set width and/or height from property dependency injection. Regardless, our navigationBarItems becomes very straight forward and more readable 🙂
NavigationView {
Text("Home View")
.navigationBarItems(
leading: NavLogo()
trailing: ProfileButton()
)
}
On iOS 13, a little hacky way to achieve this:
private var logo: some View {
Image("logo-image")
}
var body: some View {
GeometryReader { g in
content()
.navigationBarTitle("")
.navigationBarItems(leading:
ZStack(alignment: .leading) {
logo.frame(width: g.size.width).padding(.trailing, 8)
HStack {
leadingItems().padding(.leading, 10)
Spacer()
trailingItems().padding(.trailing, 10)
}
.frame(width: g.size.width)
}
)
}
}
Try the following.
struct ContainerView: View {
var body: some View {
VStack {
Image(systemName: "person.crop.square")
ContentView()
}
}
}
It worked for me.
Make sure you change ContentView to ContainerView inside SceneDelegate.swift before running on simulator or device.

Resources