SwiftUI: Problem with Tabviews and NavigationViews when TabViews is not main page - swift

Good afternoon,
I have a problem on my app with the NavigationViews. When launching the app for the very first time, users are displayed a LogIn and Sign Up page (which does not have a TabView) and once they Log-In they are directed to a TabView with different sections (Home, Chats, etc...).
The main problem that I have is that when changing from the Login view to the TabView,the NavigationTitle that is set is only that of HomeView and when changing to a different tab, the title does not change. How could I solve this problem, how do other apps deal with this?
Thank you.
File LoginView
import SwiftUI
struct LoginView: View {
var body: some View{
NavigationView{
NavigationLink(destination: TabView, label: Text("Login")
}
}
}
File TabView
import Swift UI
struct TabView: View {
var body: some View{
TabView {
HomeView()
.tabItem{
//Not relevant code
}
ChatView()
.tabItem{
//Not relevant code
}
}
}
}
File HomeView
import SwiftUI
struct HomeView: View {
var body: some View {
ZStack{
//Not relevant code
}
.navigationBarTitle("Home")
}
}
File ChatsView
import SwiftUI
struct ChatsView: View {
var body: some View {
ZStack{
//Not relevant code
}
.navigationBarTitle("Chats")
}
}

struct ContentView: View {
enum Tab {
case homeView
case chatView
}
#State var selecion: Tab = .homeView
#State var title = "House"
var body: some View{
TabView(selection: $selecion){
HomeView()
.tabItem{
//Not relevant code
Image(systemName: "house")
}
.tag(Tab.homeView)
.onAppear {
title = "House"
}
ChatsView()
.tabItem{
//Not relevant code
Image(systemName: "star")
}
.tag(Tab.chatView)
.onAppear {
title = "Chat"
}
}
.navigationTitle(title)
}
}

Related

NavigationLink inside .searchable does not work

I understand its new, but this seems like pretty basic functionality that is not here. When implementing a .searchable in the new iOS 15, it would seem that a NavigationLink does not work, at all.
Ideally, the searchable would produce a filtered list with a ForEach and for each item, a Nav Link could take you to another view based on your selection. The ForEach and list works and looks beautiful, it just wont take you anywhere.
Watching WWDC21, they talk an awful lot about .searchable, but very little demonstration/example is given..
Here is a simple example, with no ForEach loop, that shows it does not work at all.. Am I missing something?
Any insight appreciated:
import SwiftUI
struct ContentView: View {
#State private var term = ""
var body: some View {
NavigationView {
Text("Hello, world!")
.padding()
}
.searchable(text: $term) {
NavigationLink(destination: SecondView()) {
Text("Go to second view")
}
}
}
}
struct SecondView: View {
var body: some View {
Text("Goodbye!")
.padding()
}
}
NavigationLink must always be inside NavigationView, no matter what. If you want to put it outside, like inside .searchable, you should use programmatic navigation with isActive.
struct ContentView: View {
#State private var term = ""
#State private var isPresenting = false
var body: some View {
NavigationView {
/// NavigationView must only contain 1 view
VStack {
Text("Hello, world!")
.padding()
/// invisible NavigationLink
NavigationLink(destination: SecondView(), isActive: $isPresenting) { EmptyView()}
}
}
.searchable(text: $term) {
Button { isPresenting = true } label: {
Text("Go to second view")
}
}
}
}
struct SecondView: View {
var body: some View {
Text("Goodbye!")
.padding()
}
}
Result:

Variable 3-Column NavigationView SwiftUI

Problem
I would like to be able to change the number of columns in a Navigation View depending on the sidebar selection. i.e. Most views will have the desired 3-column layout (sidebar > list > detail) but one will have a two column layout (sidebar > detail). I tried to set this up directly in the top layer of the navigation view but this didn't change anything.
NavigationView{
SidebarView()
if selection != .explore {
ListView()
}
DetailView()
}
In the above example, if the selection is 'explore' there should only be a sidebar and a detail view.
Any ideas on how to achieve this?
Code to reproduce
I would want "searchView" to take up the full width. Meaning just a sidebar and search view should appear
Run on macOS or iPadOS
import SwiftUI
enum SidebarSelection {
case library
case notes
case search
}
struct ContentView: View {
#State var selection : SidebarSelection? = SidebarSelection.library
var body: some View {
NavigationView {
List {
NavigationLink(destination: ListView(), tag: SidebarSelection.library, selection: $selection){
Label("Library", systemImage: "book")
}
.tag(SidebarSelection.library)
NavigationLink(destination: ListView(), tag: SidebarSelection.notes, selection: $selection){
Label("Notes", systemImage: "doc.text")
}
.tag(SidebarSelection.notes)
NavigationLink(destination: SearchView(), tag: SidebarSelection.search, selection: $selection){
Label("Search", systemImage: "magnifyingglass")
}
.tag(SidebarSelection.search)
}
.listStyle(SidebarListStyle())
Text("List View")
if selection != .search {
Text("Detail View")
}
}
}
}
struct ListView: View {
var body: some View{
List {
ForEach(0..<10){ index in
NavigationLink(destination: Text("DetailView: \(index)")){
Text("Link to \(index) detail view")
}
}
}
}
}
struct SearchView: View {
var body: some View {
Text("Full width search view")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
What you can do is quite easy actually, to achieve this, you just have to be bit more specific for SwiftUI and use the #ViewBuilder property wrapper.
struct ContentView: View {
#ViewBuilder
var body: some View {
if(twoColumns == true){
NavigationView{
SidebarView()
DetailView()
}
} else {
NavigationView{
SidebarView()
ListView()
DetailView()
}
}
}
}

SwiftUI TabView + NavigationView navbar doesn't show up

I started to use SwiftUI after a couple years of UIKit.. This is not a piece of cake lol.
Alright, so I am trying to build an app that has a tab bar with 2 elements. Each Tab with contain a ViewController (View now) and they will be embedded in a NavigationController (NavigationView now)
The actual result is this
and I am expecting to have a nav bar with a title set to Home.
Could you explain me what I do wrong here? i followed the documentation and a couple tutorials, and I don't seem to do differently.
import SwiftUI
struct TabBarView: View {
var body: some View {
TabView() {
RedView()
.tabItem({
Image(systemName: "house.fill")
Text("Home")
})
.tag(0)
BlueView()
.tabItem({
Image(systemName: "dollarsign.square.fill")
Text("Trade")
})
.tag(1)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
TabBarView()
}
}
struct RedView: View {
var body: some View {
NavigationView {
List {
Text("test")
}
}
.navigationBarTitle("Home")
}
}
struct BlueView: View {
var body: some View {
NavigationView {
List {
Text("test2")
}
}
.navigationBarTitle("Trade")
}
}
This is the file that contains everything at the moment. Thanks in advance for any future help!
The .navigationBarTitle should be inside NavigationView
struct RedView: View {
var body: some View {
NavigationView {
List {
Text("test")
}
.navigationBarTitle("Home") // << here !!
}
}
}

Two UINavigationControllers after using NavigationLink in sheet

I have a modal sheet that is presented from my home view as such:
Button(action: {
...
}) {
...
}
.sheet(isPresented: ...) {
MySheetView()
}
In MySheetView, there is a NavigationView and a NavigationLink to push another view onto its view stack (while I'm on MySheetView screen and use the view inspector, there's only one UINavigationController associated with it which is what I expect).
However, as soon as I get to my next view that is presented from MySheetView using the NavigationLink, and I use the view hierarchy debugger, there are TWO UINavigationControllers on-top of each other. Note, this view does NOT have a NavigationView inside it, only MySheetView does.
Does anyone know what's going on here? I have a feeling this is causing some navigation bugs im experiencing. This can be easily reproduced in an example app with the same structure.
Ex:
// These are 3 separate SwiftUI files
struct ContentView: View {
#State var isPresented = false
var body: some View {
NavigationView {
Button(action: { self.isPresented = true }) {
Text("Press me")
}
.sheet(isPresented: $isPresented) {
ModalView()
}
}
}
}
struct ModalView: View {
var body: some View {
NavigationView {
NavigationLink(destination: FinalView()) {
Text("Go to final")
}
}
}
}
struct FinalView: View {
var body: some View {
Text("Hello, World!")
}
}
I don't observe the behaviour you described. Used Xcode 11.2. Probably you need to provide your code to find the reason.
Here is an example of using navigation views in main screen and sheet. (Note: removing navigation view in main screen does not affect one in sheet).
import SwiftUI
struct TestNavigationInSheet: View {
#State private var hasSheet = false
var body: some View {
NavigationView {
Button(action: {self.hasSheet = true }) {
Text("Show it")
}
.navigationBarTitle("Main")
.sheet(isPresented: $hasSheet) { self.sheetContent }
}
}
private var sheetContent: some View {
NavigationView {
VStack {
Text("Properties")
.navigationBarTitle("Sheet")
NavigationLink(destination: properties) {
Text("Go to Inspector")
}
}
}
}
private var properties: some View {
VStack {
Text("Inspector")
}
}
}
struct TestNavigationInSheet_Previews: PreviewProvider {
static var previews: some View {
TestNavigationInSheet()
}
}

How to trigger sheet on TabView click

How can I show a sheet when I click a tab in TabView? All the examples on the internet use a Button to trigger an update but I want to make the sheet appear when a user clicks one of the tabs in TabView.
I tried changing the boolean state variable in a tabbed view by adding .onAppear(), but it doesn't seem to work.
struct ContentView: View {
#State var showSheet: Bool = false
var body: some View {
return TabView {
HomeView()
.tabItem {
Image(systemName: "house")
}
}
.sheet(isPresented: self.$showSheet) {
SheetView(isShown: self.$showSheet)
}
}
}
In the above example, I basically want SheetView to show up when I click the tab. I don't want to replace HomeView with SheetView since I want it to be a sheet instead of static view. Thanks!
This can be achieved, albeit rather hackishly, by moving your State variables up one level and controlling the flow within a Group. Here, I just moved them to the app state for simplicity.
final class AppState: ObservableObject {
#Published var shouldShowActionSheet: Bool = true
#Published var selectedContentViewTab: ContentViewTabs = .none
}
In SceneDelegate.swift:
. . .
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(AppState()))
. . .
And finally in your view:
public enum ContentViewTabs: Hashable {
case none
case home
}
struct ContentView: View {
#EnvironmentObject var appState: AppState
var body: some View {
Group {
if (appState.selectedContentViewTab == .home && self.appState.shouldShowActionSheet) {
Text("").sheet(isPresented: self.$appState.shouldShowActionSheet, onDismiss: {
self.appState.shouldShowActionSheet.toggle()
self.appState.selectedContentViewTab = .none
}, content: {
Text("Oll Korrect, Chaps!")
})
} else {
TabView(selection: self.$appState.selectedContentViewTab) {
Text("First View")
.font(.title)
.tabItem {
VStack {
Image("first")
Text("First")
}
}.tag(ContentViewTabs.home)
}
}
}
}
}