Open More Navigation Tab in Swift - swift

I have a UIHostingController which contains my SwiftUI View. I want to return to the More Tab from the UIHostingController.
I tried calling
dismiss(animated: true, completion: nil)
which doesn't work. I tried changing the tabbar's selection, but this of course never goes to the more tab.
self.tabBarController!.selectedIndex = 5
I presume there is an easy function to make it pop up over my view, I just can't find it.
Edit:
To explain further, I have a storyboard with several ViewControllers. One is a UIHostingController. Perhaps that detail doesn't matter, I am trying to open the list of 'more' items from a ViewController with Swift. The UIHostingController though uses a custom navigation, so the default back buttons aren't relevant.
Update:
The closest code I have found is:
self.tabBarController?.selectedViewController = tabBarController?.moreNavigationController
This however did not appear to work, but by calling the code below. I was able to flicker show the moreViewController.
self.tabBarController?.selectedViewController = tabBarController?.moreNavigationController.popViewController(animated: true)

I don't know how you implemented the tabView but in SwiftUI the boilerplate code is like below:
struct ContentView: View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
Text("First View")
.font(.title)
.tabItem {
VStack {
Image("first")
Text("First")
}
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
VStack {
Image("second")
Text("Second")
}
}
.tag(1)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here you can see the selection is 0, if you change it to 1 it will go to second tab when you open the view. Hope this help

Finally discovered it. This is how you return to the more controller, which is just the root view.
self.navigationController?.popToRootViewController(animated: true)

Related

Hide navigation bar in all the views, swiftUI

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.

NavigationView inside a TabView Swift UI

I'm just picking up SwiftUI after a long break but I don't understand why I can't place a Navigation View within a Tab View.
I want my Navigation View to be a .tabItem so the view appears as part of the main app navigation so I'm trying this :
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
.tabItem {
Text("Home")
}
Text("Tab bar test")
.navigationBarTitle("Page One")
}
}
This doesn't work with error message
Cannot infer contextual base in reference to member 'tabItem'
But when I try this
var body: some View {
TabView {
NavigationView {
Text("Tab bar test")
.navigationBarTitle("Page One")
}
}
.tabItem {
Image(systemName: "1.circle")
Text("Home")
}
}
It builds fine but the tab bar doesn't show up.
My primary question which I'm hoping would be useful to others, is ...
Why can't I make a make a Navigation View a tab bar item by nesting .tabItem directly inside the Navigation View (as per my first example)?
I think it's a similar question to this one but there's no code there. And then quite similar to this one but they seem to be using .tabItem directy with ContentView like I want to with NavigationView but that isn't building!?
I'm probably misunderstanding something simple but I don't get this at all at the moment.
Do this for a better overview:
ContentView:
struct ContentView : View {
var body: some View {
TabView {
FirstView()
.tabItem {
Image(systemName: "folder.fill")
Text("Home")
}
SecondView()
.tabItem {
Image(systemName: "folder.fill")
Text("SecondView")
}
}
}
}
FirstView
struct FirstView: View {
var body: some View {
NavigationView {
Text("FirstView")
.navigationBarTitle("Home")
}
}
}
}
SecondView
struct SecondView: View {
var body: some View {
NavigationView {
Text("SecondView")
.navigationBarTitle("Home")
}
}
}
}
.tabItem should be used as a modifier on the view that represents that tab.
In the first example, this doesn't work because of a syntax error -- you're trying to use it on the opening of a closure in NavigationView {, when instead you want it on the outside of the closing brace: NavigationView { }.tabItem(...)
In the second example, you're using .tabItem as a modifier on the entire TabView instead of the NavigationView.
Both of your examples may have revealed what was going on more obviously if you indent your code so that you can see the hierarchy. Trying selecting your code in Xcode and using Ctrl-I to get Xcode to properly format it for you.
Here's a working version:
struct ContentView : View {
var body: some View {
TabView {
NavigationView {
Text("Tab bar test")
.navigationBarTitle("Page One")
}
.tabItem { //note how this is modifying `NavigationView`
Image(systemName: "1.circle")
Text("Home")
}
}
}
}

SwiftUI: showing keyboard in a tab view adds a weird space below second tab view

This sounds a bug in SwiftUI's NavigationView and TabView, when I have a TabView with (let's say) 2 tabs, the first tab has a TextField and the second tab has a NavigationView, follow these steps to produce the bug:
show the keyboard by tapping on the text field.
press on the "Return" button to hide the keyboard.
go to tab 2.
notice the weird bottom added space below the view, which approximately equals the height of the keyboard.
-Note1: if you do any of the following the bug won't be reproduced:
once the app launches, open tab 2, return to tab 1 and show the keyboard.
remove NavigationView from tab 2
show the keyboard one more time in tab 1
Note2:
I use GeometryReader in tab 2 to show the whole view area by a yellow color.
working code sample (just copy-paste it to try):
struct ContentView: View {
var body: some View {
TabView {
View1()
.tabItem { Text("View1") }
.tag(1)
View2()
.tabItem { Text("View2") }
.tag(2)
}
}
}
struct View1: View {
#State private var myText = ""
var body: some View {
VStack {
Text("this is view 1")
TextField("Enter Value", text: $myText)
}
}
}
struct View2: View {
var body: some View {
NavigationView {
GeometryReader { reader in
Text("this is view 2")
.onAppear{
print("view 2 on appear")
}
}
.background(Color.yellow)
}
}
}
screenshot:
Is there a way to workaround this problem without having to remove the NavigationView, I tried every possible solution but couldn't a find a clue to avoid it ?

SwiftUI Navigation through multiple screens

I'm a bit confused on how navigation works in SwiftUI. Does only the view starting the navigation need a NavigationView? I have one view with a NavigationView that has a NavigationLink to a second view. The second view then has a NavigationLink to a third and final view.
However, when my second view navigates to my third view, I get this message in the logs:
unbalanced calls to begin/end appearance transitions for <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7f85d844bf90>.
I don't know if I'm handling navigation through multiple screens correctly and I'm getting some really odd behavior where pressing Next on my second screen takes me back to my first somehow...
//This is the link in my first view, my seconds link is the same except it does to my next step and the tag is different
NavigationLink(
destination: PasswordView(store: self.store),
tag: RegisterState.Step.password,
selection: .constant(store.value.step)
)
Navigation is a little bit tricky in SwiftUI, after creating one navigationview you don't need to create again in your 2nd or 3rd view. I am not sure how you creating it firstly. Here is an example how navigation is working.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SecondView()) {
Text("Show Second View")
}.navigationBarTitle("FirstView", displayMode: .inline)
}
}
}
}
struct SecondView: View {
var body: some View {
NavigationLink(destination: ThirdView()) {
Text("Show Third view")
}.navigationBarTitle("SecondView", displayMode: .inline)
}
}
struct ThirdView: View {
var body: some View {
Text("This is third view")
.navigationBarTitle("ThirdView", displayMode: .inline)
}
}

SwiftUI: NavigationLink not working if not in a List

I guess it might be a bug in beta 3 as the NavigationView is all broken. But a view like that:
struct GenreBadge : View {
#EnvironmentObject var store: Store<AppState>
let genre: Genre
var body: some View {
NavigationLink(destination: MoviesGenreList(genre: genre).environmentObject(store)) {
RoundedBadge(text: genre.name)
}
}
}
is not triggering any push in the navigation stack. The view doens't seems interactive at all. If anyone found a workaround would be good, unless Apple is documenting this behaviour I would consider it broken until beta 4.
There seems to be a bug with the navigation link in Xcode version 11.3(11C29) which I have reported to Apple.
Note: This problem only appears in simulator. It works fine on a real device. Thanks to #djr
The below code works as expect the first time you use the navigation link. Unfortunately it becomes unresponsive the second time around.
import SwiftUI
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SomeView()) {
Text("Hello!")
}
}
}
}
}
struct SomeView: View {
var body: some View {
Text("Detailed View")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Are you actually inside a NavigationView? The following works. But if I missed your point, maybe you can share a little more code.
import SwiftUI
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SomeView()) {
Text("Go!")
}
}
}
}
}
struct SomeView: View {
var body: some View {
Text("Detailed View Here!")
}
}
NavigationLink
A button that triggers a navigation presentation when pressed. This is a replacement for pushViewController
NavigationView {
NavigationLink(destination:
Text("Detail")
.navigationBarTitle(Text("Detail"))
) {
Text("Push")
}.navigationBarTitle(Text("Master"))
}
Or make it more readable by use group destination into it own view DetailView
NavigationView {
NavigationLink(destination: DetailView()) {
Text("Push")
}.navigationBarTitle(Text("Master"))
}
In Xcode 11.3.1, I experienced the same issue but I just had to quit xcode and restart my computer. This apparently fixed an issue (of course its 2021 right now) but I was able to follow apple's swiftui tutorial without any issues. I copied this code and tried it out... worked fine for me. Maybe the issue is your "play" button on the bottom right of your screen was toggled.
Try this:
VStack{
NavigationLink(destination: DetailView()) {
Text("Push")
}.navigationBarTitle(Text("Your Text")).isDetailLink(false)
}
Try this:-
var body: some View {
NavigationView {
NavigationLink(destination: YourView()) {
Text("Go!")
}
}
}