Draw a view over the navigation bar - ios

I'm working on a bottom sheet that can be invoked from any other screen. The bottom sheet will be displayed on top of a half-opaque overlay and I would like the overlay to render full screen over any other view including the navigation bar and the tab bar.
However, I can't seem to be able to figure out how to get the content of the navigation bar to be behind the overlay. Here is what a demo of my current implementation looks like. As you can see, it's possible to interact with the content of the navigation bar even though it is visually displayed behind the overlay.
Half Screen
Full Screen
Back button is still active
And here is the simplified code of my current implementation:
import SwiftUI
struct MainNavigationView: View {
var body: some View {
NavigationView {
NavigationLink(destination: AnoterView()) {
Text("Navigate to the next screen")
}
}
}
}
struct AnoterView: View {
var body: some View {
ZStack {
Color(uiColor: .red)
.edgesIgnoringSafeArea(.all)
.navigationTitle("Test")
.navigationBarTitleDisplayMode(.inline)
ViewWithOverlay()
}
}
}
struct ViewWithOverlay: View {
var body: some View {
ZStack {
// I'd like this overlay to be rendered over the navigation bar
Color(uiColor: .blue)
.edgesIgnoringSafeArea(.all)
Color(uiColor: .green)
}
}
}
And the outcome:
As you can see, while the blue color, which represent my overlay, is drawn over the red color, the title and the back button are still displayed on top of the blue color.
I understand why this is happening, but I cannot think of any workaround in SwiftUI to fix this that can be invoked from any view.
Any help is appreciated.

If you want to overlay everything then it should be on root, including over NavigationView as well, ie.
ZStack {
NavigationView {
Color(uiColor: .red).edgesIgnoringSafeArea(.all)
}
ViewWithOverlay() // << here !!
}
.edgesIgnoringSafeArea(.all)

One thing you can do is to put the NavigationView inside a ZStack. This way it will be in a lower layer hidden by the layer above. Here is the code that completely hides the NavigationBar on the tap of the button.
struct ContentView: View {
#State private var isPresented: Bool = false
var body: some View {
ZStack {
NavigationView {
Text("Hello World")
.navigationTitle("Welcome")
}
VStack {
}.frame(maxWidth: isPresented ? .infinity: 0, maxHeight: isPresented ? .infinity: 0)
.background(.green)
Button("Animate") {
withAnimation {
isPresented.toggle()
}
}
}
}
}

Related

Ignore SafeArea inside NavigationView

I am creating simple app with swiftui. In my app, I have 2 screens "ContentView" and "Home".
In my Content View,
struct ContentView : View {
var body: some View {
NavigationView {
ZStack {
//Some views
NavigationLink(isActive: self.$routeHome, destination: {Home},label: {
Text("Home")})
.navigationBarTitle("title")
}
.navigationBarTitle("Page1")
}
}
}
and In Home,
struct Home : View {
var body: some View {
ZStack {
Color.red.edgesIgnoringSafeArea(.all)
ScrollView{
//some views
}
}.navigationBarTitle("Home")
}
}
the Red color takes entire screen at first. but when I scroll, the NavigationView area is not in red color anymore. In my actual code, I want to show red color for all spaces when I click on button inside scrollview. Please help me to accomplish this in SwiftUI.
NavigationBar is colored by top child scrollView's offset.
If you want to always hide navigationBar background, you can use toolbarBackground() like following code.
struct Home : View {
var body: some View {
...
.navigationBarTitle("Home")
.toolbarBackground(.hidden, for: .navigationBar) // Added
}
}
P.S. I tried this codes with iPhone simulator 16.2
To ignore the safe area inside a NavigationView in SwiftUI, you can use the edgesIgnoringSafeArea(_:) modifier and pass it the .top and/or .bottom edges, like this:
NavigationView {
VStack {
// your content here
}
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
}
This will allow the content of the VStack to extend under the top and bottom safe areas of the NavigationView. Note that this may cause the content to overlap with the system status bar and the navigation bar, so you may need to adjust the layout or add additional padding or spacing as needed.
Alternatively, you can use the .navigationBarHidden(true) modifier to hide the navigation bar entirely and allow the content to extend to the edges of the screen.
NavigationView {
VStack {
// your content here
}
.navigationBarHidden(true)
}

SwiftUI - Empty bottom bar after tapping NavigationLink

I have a NavigationView with a toolbar that contains a ToolBarItem with a .bottomBar placement and a search field. This NavigationView contains a ScrollView with content that exceeds the screen's vertical size, which means that the bottom bar has a background, as seen below:
When the user taps the "Root View" text element they navigate to a new view, in this case, just another text displaying "Detail View". The problem, however, is that the bottom toolbar's background remains in the screen instead of vanishing as expected. See the screenshot below:
This behavior is not seen if I remove the search bar or shrink the ScrollView's height to fit the vertical dimension of the device. I tried googling this issue to see if it was a known bug with a workaround but maybe I'm not searching the right keywords. How can I fix this issue?
Please see the bare minimum to replicate the issue below:
struct BugView: View {
#State var searchPattern: String = ""
var body: some View {
NavigationView {
ScrollView {
VStack {
NavigationLink(destination: Text("Detail View")) {
Text("Root View").foregroundColor(Color.blue)
}
Spacer()
Text("Root View Bottom").foregroundColor(Color.blue)
}.frame(maxWidth: .infinity, minHeight: 1000)
}
.searchable(text: self.$searchPattern, prompt: "Search Here")
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
Text("Toolbar text")
}
})
}
}
}
Setup:
XCode Version: 13.4.1
Simulator: iPhone 13
You can use the .toolbar(.hidden, for: .bottomBar) for the destination view as shown in the code below:
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink {
Text("Destination View")
} label: {
// hiding the toolbar for the destination view
Text("Root View").toolbar(.hidden, for: .bottomBar)
}
}.toolbar {
ToolbarItem(placement: .bottomBar) {
Text("Toolbar Text")
.background {
Color.gray
}
}
}
}
}

Remove shadow effect on background color during page transition

I'm making an application with SwiftUI that has a blue background color, so I did that by something like this:
// MainView
var body: some View {
NavigationView {
ZStack {
Color("ThemeColor").edgesIgnoringSafeArea(.top)
Color("BgColor")
VStack {
NavigationLink(destination: DetailView() {
Text("View Details")
})
// ...
}
}
}
}
// DetailView
var body: some View {
ZStack {
Color("ThemeColor").edgesIgnoringSafeArea(.top)
Color("BgColor")
VStack {
Text("Details Page")
// ...
}
}
}
I gave the status bar a blue background(ThemeColor) and the page itself a white(BgColor) background. This works as expected, but when I press a Navigation Link, the animation is a bit weird:
A shadow effect appears on the status bar, which makes the animation look awkward. Can I remove this shadow effect? It would be better if the shadow on the page itself (the white part) will be not removed.

How to change background color of the NavigationView in SwiftUI?

I'm trying to change background color of the NavigationView (not navigation bar) using this code:
NavigationView {
Text("Text")
}
.background(Color.clear)
But it doesn't work.
Also, I tried to change UIView appearance:
UIView.appearance().backgroundColor = UIColor.black
But it doesn't work too.
The actual result is presented below:
The desired result is:
Does anyone know how to do this?
First look at this result:
As you can see, you can set the color of each element in the View hierarchy like this:
struct ContentView: View {
init(){
UINavigationBar.appearance().backgroundColor = .green
//For other NavigationBar changes, look here:(https://stackoverflow.com/a/57509555/5623035)
}
var body: some View {
ZStack {
Color.yellow
NavigationView {
ZStack {
Color.blue
Text("Some text")
}
}.background(Color.red)
}
// iOS 16 - No need for tweaking the appearance
/* .toolbarBackground(.green, in: .navigationBar) */
}
}
And the first one is window:
window.backgroundColor = .magenta
The issue you faced is we can not remove the background color of SwiftUI's HostingViewController (yet), so you can't see the navigationView (What you called) through the views hierarchy. You should wait for the API or try to fake the navigation view (not recommended).
If you just embed your content in the NavigationView within a ZStack you should be able to throw the color in underneath your main content.
struct ContentView : View {
var body: some View {
NavigationView {
ZStack {
Color.red.edgesIgnoringSafeArea(.all)
ScrollView {
Text("Fatemeh")
}
.navigationBarTitle("title")
}
}
}
}
Happy coding ;)

Placing interactable views in front of a NavigationView's hidden navigation bar

I have a NavigationView with a NavigationButton inside of it, but I cannot get the NavigationButton to be at the top of the screen and still be able to be pressed, even though the navigation bar is hidden.
This code:
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
NavigationButton(destination: Text("Button Clicked")) {
Text("Hello World")
.background(Color.yellow)
}
Spacer()
}
}
.navigationBarHidden(true)
}
}
Looks like , but I want it to look like .
I've tried adding a negative padding to the top of the VStack (with .padding([.top], -95), and it visually works, but then I can't interact with the button by tapping it (I think it is behind the hidden navigation bar). I've tried setting the VStack's zIndex to 10000 to solve that, but it still didn't work. Is there a way for me to move the button up to the top while still making sure that the button recognizes when it is being tapped?
Add a navigationBarTitle before hiding your navigation bar:
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
NavigationButton(destination: Text("Button Clicked")) {
Text("Hello World")
.background(Color.yellow)
}
Spacer()
}
.navigationBarTitle(Text("Title")) // Add this line
.navigationBarHidden(true)
}
}
Add this modifier to your NavigationView edgesIgnoringSafeArea(.top).

Resources