I have an Issue where my text disappears behind the navigation bar. The navigation bar is made visible once the user clicks on Settings (or any other menu button). It shows up and the content is visible but then when done loading the new view the text disappears behind the bar. Any solutions?
I change the status of the navigation bar being visible with .onAppear and .onDisappear of views that are root level.
Code something like this:
struct ContentView: View {
#State public var navBarHidden = true
var body: some View {
NavigationView{
VStack{
ZStack(alignment: .center){
WhiteImage().onAppear{self.navBarHidden = true} //Here only seen as white background
BottomButtons().onDisappear{self.navBarHidden = false}
ProfileInvoke().navigationBarTitle("").navigationBarHidden(self.navBarHidden)
}
}
}
}
}
//The buttons are done with such a construct
struct MenuButton: View {
var buttonText: String
var buttonCallView: AnyView
var body: some View {
NavigationLink(destination: self.buttonCallView) {
Text(self.buttonText)
}.padding()
}
}
//Population of a button
MenuButton(buttonText: "My Favourites", buttonCallView: AnyView(MyFavouritesView().navigationBarTitle(Text("My Favourites"), displayMode: .inline)))
// The settings view where the title disappears
struct SettingsView: View {
var body: some View {
HStack(alignment: .top){
VStack(alignment: .leading){
Text("General").bold()
Divider()
Spacer()
}.padding()
Spacer()
}
}
}
I have the feeling that it has something to do with the .onAppear and .onDisappear where I set the status of the navigation bar being hidden or not. Ain't sure tho.
Related
Problem: Custom TabView does not recognise NavBar so its being pushed out of view when keyboard is active.
This is container code that wraps content and TabBarItems in Zstack. As content is made this way it contains all NavBar informations and that NavBar is being treated like any other view, its being pushed upwards when keyboard is active.
struct CustomTabBarContainerView<Content:View> : View {
#Binding var selection: TabBarItem
#State private var tabs: [TabBarItem] = []
let content: Content
init(selection: Binding<TabBarItem>, #ViewBuilder content: () -> Content){
self._selection = selection
self.content = content()
}
var body: some View {
containerVersion2
}
}
private var containerVersion2 : some View{
ZStack(alignment: .bottom){
content
.zIndex(0)
.ignoresSafeArea()
VStack(spacing:15){
CustomTabBarView(tabs: tabs, selection: $selection, localSelection: selection).zIndex(0)
}
}
}
This is AppTabBar that is being used with custom implementation:
struct AppTabBarView: View {
#State private var selection: String = "home"
#State private var tabSelection: TabBarItem = .chat
var body: some View {
//instead of apple default TabView we are going to use our custom that looks nearly the same
CustomTabBarContainerView(selection: $tabSelection){
ChatsView()//is wrapped with NavigationView as it contains chats to enter called ChatView()
.tabBarItem(tab: .chat, selection: $tabSelection)//code for this is reddudant but this is also CUSTOM not default
LogBackgroundView()
.tabBarItem(tab: .upcoming, selection: $tabSelection)
...
}
One possible solution is to wrap CustomTabBarContainerView with NavigationView here in AppTabBar struct and delete ChatsView NavigationView, that way NavBar works perfectly but I think it ugly solution for producion code.
I've tried adding .ignoresSafeArea(.keyboard, edges: .bottom) to ChatView that is being navigated to but only efect i got was that only Hstack where is TextEdit and sendButton stays in place(not being pushed by keyboard) but scrollView where is conversation and NavBar above it is still being pushed.
struct ChatView: View {
var body: some View {
ZStack{
VStack{
Spacer()
messagesView
.padding(.vertical)
}
VStack(spacing:0){
chatBottomBar
}
}
.background(chatBackground,ignoresSafeAreaEdges: [.bottom])
.ignoresSafeArea(.keyboard, edges: .bottom)
.navigationBarTitleDisplayMode(.inline)
This is customTabBar resource I used as its concept is to be as similar as possible to apple TabView Making custom TabView
Currently I am working on SwiftUI project. I want to hide the build-in navbar. For this purpose I have to add these lines,
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
to each of view before pushing it into navigation controller in SwiftUI.
NavigationLink(destination:
ForgotPasswordView()
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
) {
Text("Forgot Password?")
.foregroundColor(.white)
}
Same will be done for LoginView
NavigationLink(destination:
LoginView()
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
) {
Text("Login")
.foregroundColor(.white)
}
So I need any generic method like we did in storyboard, hide it from root view and no child will have the navbar on top.
Updated for iOS 16+
You can wrap the view by creating a new view that accepts a child view, which adds all the necessary modifiers so that you don't have to repeat it every time.
struct NavigationWrapper<Content: View>: View {
#ViewBuilder var childView: Content
var body: some View {
childView
.toolbar(.hidden)
}
}
Now, you can use that wrapper for your navigation link's destination.
Here is an example:
struct ContentView: View {
var body: some View {
NavigationStack {
List {
ForEach(0..<10) { i in
NavigationLink("Row \(i)") {
NavigationWrapper {
Text("Detailed view for \(i)th row.")
}
}
}
}
.navigationTitle("RowGap")
.toolbar(.hidden)
}
}
}
It hides out the navigation bar and toolbar completely.
I have a view that is a TabView with the style PageTabViewStyle(indexDisplayMode: .always which looks great for my use case however the page indicator is bumping right up to the safe area near the bottom of the screen and it looks bad. I'd like to move the page indicator up to some n value. here's a sample of my view to reproducing it. If this view were built on any device without a Home Button, it will ride on top of the home indicator line.
var body: some View {
ZStack {
TabView(selection: $homeVM.selectedPageIndex) {
// Any number of views here.
}
.frame(width: UIScreen.main.bounds.width)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .never))
}.edgesIgnoringSafeArea(.all)
}
I attempted to add the padding to the ZStack, which does work but then my TabView is cut off on the bottom, which means my cells disappear prematurely.
Here's an image for what I'm trying to fix. Notice the page indicator sits on the home bar indicator. I need the indicators pushed up, without pushing up the background ScrollView
Update #1
This view is being presented by a base view that I use to handle my navigation stack. The view is as follows. The important thing to note here is the .ignoresSafeArea() that I have on this view. I did that because it's a containing view for my eventual TabView to be presented from. Interestingly if I remove this modifier the indicators move up to a more manageable position, but then my form becomes clipped at the top and bottom of the device when scrolling, and that's not ideal.
struct BaseLaunchView: View {
#StateObject var baseNavVM = BaseLaunchViewModel()
#State var shouldLogin = false
#State var shouldRegister = false
var body: some View {
VStack {
switch baseNavVM.loggedIn {
case true:
HomeView()
default:
NavigationView {
VStack{
Spacer()
VStack(spacing: 30) {
VStack {
Text("Stello")
.font(Fonts.title)
Text("Life Groups")
.font(Fonts.body)
}
StelloDivider()
Text("Connect with like minded people, to fellowship and find your home.")
.font(Fonts.subheading)
.multilineTextAlignment(.center)
}
Spacer()
NavigationLink(destination: RegisterOptionsView(isLoggingIn: true), isActive: $shouldLogin) {
Button(action: {
shouldLogin.toggle()
}, label: {
Text("Login")
.font(Fonts.button)
}).buttonStyle(StelloFillButtonStyle())
}
NavigationLink(destination: RegisterOptionsView(isLoggingIn: false), isActive: $shouldRegister) {
Button(action: {
shouldRegister.toggle()
}, label: {
Text("Register")
.font(Fonts.button)
}).buttonStyle(StelloHollowButtonStyle())
}
}
}.accentColor(.black)
}
}
.padding()
.environmentObject(baseNavVM)
.ignoresSafeArea()
}
}
I'm using Xcode 12 with deployment for iOS 14.0.
My home screen has a NavigationView
Within the NavigationView there is a TabView (with 4 tabs)
Within each tab are subviews that have buttons and NavigationLinks
The navigation on the app is functioning correctly (when I click a NavigationLink on one of the subviews, it navigates to the correct view and when I click the back button, it dismisses the view.) However, when I click the back button, the console prints the following error:
Trying to pop to a missing destination at /Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-103/Shared/NavigationBridge_PhoneTV.swift:337
Aside from the error log, the app is functioning fine, so I'm planning to just ignore the error for now... but I'm wondering what it means? I don't have anything within my code named "Monoceros". I'm guessing it has something to do with the TabView being a subview of the NavigationView?
EDIT:
Several months later, this issue still persists. Here is reproducible code. Open the ContentView(), on the FirstScreen() click on the NavigationLink, then click the back button. It will print out Monoceros lol
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
FirstScreen()
.tabItem {
Text("One")
Image(systemName: "house.fill")
}
Text("Second Screen")
.tabItem {
Text("Two")
Image(systemName: "heart.fill")
}
}
}
}
}
struct FirstScreen: View {
var body: some View {
NavigationLink("Click here", destination: Text("Final Screen"))
// Click the back button on FinalScreen prints:
//Trying to pop to a missing destination at /Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-120/Shared/NavigationBridge_PhoneTV.swift:341
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Unfortunately this is an active issue with a TabView placed inside NavigationView.
The error would not manifest itself if you placed your NavigationView inside the TabView, but this of course would lead to the tabs being displayed from within your Final Screen, which you are probably trying to avoid.
There is currently no work-around for this, and as of to date we need to wait for Apple to properly implement the corresponding .navigationBarHidden() of TabViews as well.
Problems and unexpected behaviours have been reported when embedding a TabView into a NavigationView, however if you have tested your app thoroughly and found no particular problems, it is safe to say that you can stick to this method.
Alternatively you will have to build a TabView component manually, like below:
import SwiftUI
enum Tab {
case house, heart
}
struct TabView: View {
#Binding var tabIdx: Tab
var body: some View {
HStack {
Group {
Spacer()
Button (action: {
self.tabIdx = .house
}) {
VStack{
Image(systemName: "house.fill")
Text("House")
.font(.system(size: 10))
}
}
.foregroundColor(self.tabIdx == .house ? .blue : .secondary)
Spacer()
Button (action: {
self.tabIdx = .heart
}) {
VStack{
Image(systemName: "heart.fill")
Text("Heart")
.font(.system(size: 10))
}
}
.foregroundColor(self.tabIdx == .heart ? .blue : .secondary)
Spacer()
}
}
.padding(.bottom, 30)
.padding(.top, 10)
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
.font(.system(size: 30))
.frame(height: 80)
}
}
struct FirstScreen: View {
var body: some View {
NavigationLink("Click here", destination: Text("Final Screen"))
.font(.system(size:20))
}
}
struct ContentView: View {
#State var tabIdx: Tab = .house
var body: some View {
NavigationView {
VStack(spacing: 20) {
Spacer()
if tabIdx == .house {
FirstScreen()
} else if tabIdx == .heart {
Text("Second Screen")
}
Spacer(minLength: 0)
TabView(tabIdx: self.$tabIdx)
}
.ignoresSafeArea()
}
}
}
The above bug is well detailed in this blog post, which you could consult for further reference and more examples.
I try to use "sheet" modifier to popup a modal view when a List cell was tapped. I find that it's ok in the List Cell View (Modal View could popup multi-times when cell be clicked), but in List View the modal view popups just once.
I have tried using Gesture and Button to trigger the popup, I find that once I use "sheet" modifier to trigger the Modal View, the Result is all the same.
struct ContentView: View {
var body: some View {
List {
ForEach(0..<5) { _ in
ListRow()
}
}
}
}
struct ListRow: View {
#State var showDetail: Bool = false
var body: some View {
HStack(spacing: 20.0) {
Button(action: {self.showDetail = true}) {
HStack {
Text("BigTitle")
.font(.largeTitle)
.fontWeight(.heavy)
Spacer()
Text("SubTitle")
.font(.headline)
.fontWeight(.medium)
.padding(.trailing, 10)
}
}
.sheet(isPresented: self.$showDetail) {
DetailView()
}
}
.frame(width: 320, height: 48)
.padding()
}
}
struct DetailView: View {
var body: some View {
Text("I am Detail View!")
.font(.largeTitle)
.fontWeight(.heavy)
}
}
I had hoped that the modal view could be popped multiple-times in the List View, but in reality the popup occurred just once.
Hope you reply!
Thank you!