How to hide navigationBar in SwiftUI when parent Views are with titles - swift

I'm trying to make a simple app using SwiftUI using NavigationView and the last View is a video player (which I obviously don't want to have a navigationBar). The thing is that every other View leading to the player has navigationBarTitle and it just stays.
What I have:
ContentView :
var body: some View {
NavigationView {
VStack {
Text("Sample")
DetailedView(data: CustomData.sample)
}
.navigationBarTitle(Text("Main"))
}
}
DetailedView:
#ObservedObject var data: CustomData
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack {
ForEach(data.array) { videoData in
NavigationLink(destination: VideoDetailed(videoData: videoData)) {
VideoRow(episode: episode)
}
}
}
}
}
VideoDetailed:
#ObservedObject var videoData: VideoData
var body: some View {
VStack {
NavigationLink(destination: PlayerContainerView(url: videoData.url)
.navigationBarBackButtonHidden(true)
.navigationBarTitle(Text("_"))
.navigationBarHidden(true)){
Image(systemName: "play.fill")
.resizable()
.foregroundColor(.white)
.aspectRatio(contentMode: .fit)
.shadow(radius: 5)
.frame(maxWidth: 50)
}
Text(videoData.description)
Spacer()
}
.navigationBarTitle(Text(videoData.title), displayMode: .inline)
}
As a result of this code I get no back button and a "_" for title with a navigation bar

You need to set the title to an empty string and the displayMode to inline for it to hide.
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(false)

Just remove this line:
.navigationBarTitle(Text("_"))
from VideoDetailed.

Related

Dismissing a SwiftUI sheet with a lot of navigation views

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

SwiftUI pushed View through Navigationview pops back after dismissing sheet

I have tabView that presents multiple tabs . in home view i have navigationView that has search button as navigationitem,that pushes to search view . inside the search view after presenting a sheet and dismissing it, searchView gets pop to home view and pushed again to top. and this causes the interface of search being misplaces.
here is my code for tabView:
struct ContentView: View {
var body: some View {
TabView {
homeView()
.tabItem { Text("Home") }
}
}
}
here is the code for HomeView:
struct homeView:View{
#State var showSearch:Bool = false
var body: some View{
NavigationView{
Text("home")
.navigationBarTitle("", displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: HStack{
NavigationLink.init("", destination: SearchContentView(), isActive: $showSearch)
Button.init("search", action: {
showSearch.toggle()
})})
}
}
}
and then this is searchView:
struct SearchContentView: View {
#State private var isplayItem:Bool = false
#State private var isEditing:Bool = false
var body: some View {
List(0..<30, rowContent: { i in
Text("\(i)th")
.onTapGesture {
isplayItem.toggle()
}
.sheet(isPresented: self.$isplayItem) {
Text("search Item \(i)")
.background(Color.blue)
.offset(x: 0, y: 0)
}
})
.navigationBarTitle("search", displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
}
}
thanks in advance.
Currently placing a NavigationLink in the navigationBarItems may cause some issues.
Try removing the NavigationLink from navigationBarItems and put it the background:
struct homeView: View {
#State var showSearch: Bool = false
var body: some View {
NavigationView {
Text("home")
// move `NavigationLink` outside `navigationBarItems`
.background(NavigationLink("", destination: SearchContentView(), isActive: $showSearch))
.navigationBarTitle("", displayMode: .inline)
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarItems(trailing: HStack {
Button("search", action: {
showSearch.toggle()
})
})
}
}
}

SwiftUI Sheet is only working once when opened from navigation bar

I have the problem that my sheet is not working as it should. When using a second NavigationView inside the SecondView then the sheet works but it looks crazy (first screenshot). When I comment out the NavigationView in SecondView the app looks correct (second screenshot), but the sheet is only working once when I open it from the Add button in navigation bar. When I use the "open sheet"-button in SecondView then the sheet works correct.
Did I do something wrong with this:
.navigationBarItems(trailing: Button("Add") {
showSheet.toggle()
})
SecondView with NavigationView:
SecondView without NavigationView (how it should look like):
This is the code I'm using:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("Start View")
.font(.title)
NavigationLink(destination: SecondView()) {
CustomButton()
}
}
}
}
}
struct CustomButton: View {
var body: some View {
HStack {
Spacer()
Text("Go To Second View")
.font(.headline)
.foregroundColor(.red)
.padding()
Spacer()
}
.background(Color.white)
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.red, lineWidth: 2)
)
.padding()
}
}
struct SecondView: View {
#State private var showSheet = false
var body: some View {
//NavigationView{
Text("Second View")
Button("open sheet", action: {
showSheet.toggle()
})
.navigationBarBackButtonHidden(true)
.navigationBarItems(trailing: Button("Add") {
showSheet.toggle()
})
.sheet(isPresented: $showSheet, content: {
SecondViewsSheet()
})
//}
}
}
struct SecondViewsSheet: View {
var body: some View {
Text("Sheet")
.navigationBarItems(trailing: Button("Test"){
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I use Xcode 12.2 with an iPhone 12 Pro simulator.

iOS 14 SwiftUI Keyboard lifts view automatically

I am using TextField in my view and when it becomes the first responder, it lefts the view as shown in the below GIF.
Is there any way I can get rid of this behavior?
Here is my code
NavigationView(content: {
ZStack{
MyTabView(selectedIndex: self.$index)
.view(item: self.item1) {
NewView(title: "Hello1").navigationBarTitle("")
.navigationBarHidden(true)
}
.view(item: self.item2) {
NewView(title: "Hello2").navigationBarTitle("")
.navigationBarHidden(true)
}
.view(item: self.item3) {
NewView(title: "Hello3").navigationBarTitle("")
.navigationBarHidden(true)
}
}.navigationBarHidden(true)
.navigationBarTitle("")
}).ignoresSafeArea(.keyboard, edges: .bottom)
// New View
struct NewView:View {
#State var text:String = ""
var title:String
var body: some View {
VStack {
Spacer()
Text("Hello")
TextField(title, text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}.padding()
.onAppear {
debugPrint("OnApper \(self.title)")
}
}
}
For .ignoresSafeArea to work you need to fill all the available area (eg. by using a Spacer).
The following will not work (no Spacers, just a TextField):
struct ContentView: View {
#State var text: String = ""
var body: some View {
VStack {
TextField("asd", text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
However, it will work when you add Spacers (fill all the available space):
struct ContentView: View {
#State var text: String = ""
var body: some View {
VStack {
Spacer()
TextField("asd", text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
Spacer()
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
If you don't want to use Spacers you can also use a GeometryReader:
struct ContentView: View {
#State var text: String = ""
var body: some View {
GeometryReader { _ in
...
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
You should apply the modifier on the ZStack, NOT the NavigationView
NavigationView(content: {
ZStack{
,,,
}.navigationBarHidden(true)
.navigationBarTitle("")
.ignoresSafeArea(.keyboard, edges: .bottom) // <- This line moved up
})
Full working example:
struct ContentView: View {
#State var text = ""
var body: some View {
VStack{
Spacer()
Text("Hello, World")
TextField("Tap to test keyboard ignoring", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding()
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
What eventually worked for me, combining answers posted here and considering also this question, is the following (Xcode 12.4, iOS 14.4):
GeometryReader { _ in
VStack {
Spacer()
TextField("Type something...", text: $value)
Spacer()
}.ignoresSafeArea(.keyboard, edges: .bottom)
}
Both spacers are there to center vertically the textfield.
Using only the GeometryReader or the ignoresSafeArea modifier didn't do the trick, but after putting them all together as shown above stopped eventually the view from moving up upon keyboard appearance.
That's what I figured out:
GeometryReader { _ in
ZStack {
//PUT CONTENT HERE
}.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
}
It seems to work for me. In this case you do not need to check iOS 14 availability.

NavigationView doesn't display correctly when using TabView in SwiftUI

Hello everyone. I'm developing a simple SwiftUI application that displays some tweets. It has a tab view with two views: the main page that will display the tweets and a secondary view.
The problem is that the main page has a NavigationView. If I choose to display only the main page, everything seems correct, but when I display it from the TabView and I scroll down, the NavigationView feels a bit weird.
As I'm not good at explaining, here you have some images:
It should be like this
But it is like this
I thought of adding .edgesIgnoringSafeArea(.top), but the NavigationView is now hidden by the notch and it doesn't make the effect.
Is there any way I can make the NavigationView display like in the first image?
Any help is appreciated. Thanks in advance.
My code
HomePageView:
struct HomePageView: View {
var body: some View {
NavigationView {
List {
//tweet code
}
.navigationBarTitle("Your feed")
}
}
}
TabView:
struct TabController: View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
HomePageView()
.tabItem {
VStack {
Image(systemName: "house.fill")
.font(.title)
}
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
VStack {
Image(systemName: "bell.fill")
.font(.title)
}
}
.tag(1)
}
}
}
Try adding .edgesIgnoringSafeArea(.top) to your tabview.
struct ContentView: View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
HomePageView()
.tabItem {
VStack {
Image(systemName: "house.fill")
.font(.title)
}
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
VStack {
Image(systemName: "bell.fill")
.font(.title)
}
}
.tag(1)
}.edgesIgnoringSafeArea(.top)
}
}