TabView resets navigation stack when switching tabs
Here's a simple example of how to preserve state for a navigation stack with a list of items at the root:
struct ContentView: View {
var body: some View {
TabView {
Text("First tab")
.tabItem { Image(systemName: "1.square.fill"); Text("First") }
.tag(0)
SecondTabView()
.tabItem { Image(systemName: "2.square.fill"); Text("Second") }
.tag(1)
}
}
}
struct SecondTabView: View {
private struct ListItem: Identifiable {
var id = UUID()
let title: String
}
private let items = (1...10).map { ListItem(title: "Item #\($0)") }
@State var selectedItemIndex: Int? = nil
var body: some View {
NavigationView {
List(self.items.indices) { index in
NavigationLink(destination: Text(self.items[index].title),
tag: index,
selection: self.$selectedItemIndex) {
Text(self.items[index].title)
}
}
.navigationBarTitle("Second tab", displayMode: .inline)
}
}
}
Unwind NavigationView to root when switching tabs in SwiftUI
You'll need to keep track of the tab selection in the parent view and then pass that into the child views so that they can watch for changes. Upon seeing a change in the selection, the child view can then reset a @State
variable that change the isActive
property of the NavigationLink
.
class NavigationManager : ObservableObject {
@Published var activeTab = 0
}
struct MyTabView: View {
@StateObject private var navigationManager = NavigationManager()
var body: some View {
TabView(selection: $navigationManager.activeTab) {
TabOne().tabItem { Image(systemName: "1.square") }.tag(0)
TabTwo().tabItem { Image(systemName: "2.square") }.tag(1)
}.environmentObject(navigationManager)
}
}
struct TabOne: View {
var body: some View {
Text("1")
}
}
struct TabTwo: View {
@EnvironmentObject private var navigationManager : NavigationManager
@State private var linkActive = false
var body: some View {
NavigationView {
NavigationLink("Go to sub view", isActive: $linkActive) {
TabTwoSub()
}
}.onChange(of: navigationManager.activeTab) { newValue in
linkActive = false
}
}
}
struct TabTwoSub: View {
var body: some View {
Text("Tapping \(Image(systemName: "1.square")) doesnt unwind this view back to the root of the NavigationView")
.multilineTextAlignment(.center)
}
}
Note: this will result in a "Unbalanced calls to begin/end appearance transitions" message in the console -- in my experience, this is not an error and not something we have to worry about
Reset NagivationView stack in TabView
That's because the View won't be rerendered. Here is a possible approach how to achieve your behavior:
You can use ProxyBinding for the TabView to detect changes and then reset the NavigationLink by changing the internal State variable.
struct ContentView: View {
@State var activeView: Int = 0
@State var showNavigation: Bool = false
var body: some View {
TabView(selection: Binding<Int>(
get: {
activeView
}, set: {
activeView = $0
showNavigation = false //<< when pressing Tab Bar Reset Navigation View
}))
{
NavigationView {
NavigationLink("Click", destination: Text("Page A"), isActive: $showNavigation)
}
.tabItem {
Image(systemName: "1.circle")
Text("First")
}
.tag(0)
Text("Second View")
.padding()
.tabItem {
Image(systemName: "2.circle")
Text("Second")
}
.tag(1)
}
}
}
TabView is not switching tabs properly in SwiftUI
You're binding your TabView's current tab to $selectedTab
, but not providing SwiftUI with any information on how to alter that value when the user changes tabs. And so, because selectedTab
hasn't changed, when the drawing system comes to review your view structure, it still concludes that you want to see the first tab.
You should add a .tag
modifier after each .tabItem
to tell SwiftUI what values represent each tab. Then, when the user selects each tab, selectedTab
will be updated and the tab choice will "stick".
For example:
TabView(selection: $selectedTab) {
ChatList(launchedChatID: appState.selectedChatID ?? "", userModel: userModel, chatModel: chatModel)
.tabItem {
Image(systemName: "message.circle.fill")
Text("Active Chats")
}
.tag(0)
UserList(userModel: userModel)
.tabItem {
Image(systemName: "person.3.fill")
Text("User List")
}
.tag(1)
ProfileView()
.tabItem {
Image(systemName: "person.crop.circle")
Text("Profile")
}
.tag(2)
}
Note that unless you're persisting the user's choice in some way (e.g., by declaring your state variable with @SceneStorage
) you can get the same effect by not using a selection
argument at all.
TabView Indicator doesn't move when page changes
The green area below does move to the right, but only 1 pixel. Try something like this example code, choose the value (200) most suited for your purpose:
.onAppear {
self.tabOffset = CGFloat(index*200) // <-- here
}
Tabview not switching tabs properly
Solution:
TabView(selection: $currentIndex) {
ForEach(0..<hvm.upcomingFilms.count) { film in
MediaImageView(mediaPath: hvm.upcomingFilms[film], poster: false)
.scaledToFill()
}
}
TabView keeping state on tab changes SwiftUI
I see what you mean now (I think).
My internet is very fast and so I could not see the ProgressView
going on.
You could try this in RickAndMortyViewModel
:
@Published var loadingone: Bool = true
@Published var loadingtwo: Bool = true
func getpageone() {
if !loadingone {return}
...
}
func getpagetwo() {
if !loadingtwo {return}
...
}
Related Topics
Nsuserdefaults Not Working on Xcode Beta With Watch Os2
Binary Operator * Cannot Be Applied to Operands of Type Int and Double
Check Password String Strength Criteria in Swift
Choosing Coredata Entities from Form Picker
Get HTML from Wkwebview in Swift
Accessing Code in Swift 3 Error
How to Use Attributed String in Swiftui
How to Compare One Value Against Multiple Values - Swift
How to Round a Double to the Nearest Int in Swift
What Does a Module Mean in Swift
Rounding a Double Value to X Number of Decimal Places in Swift
Round Trip Swift Number Types To/From Data
Nsdate() or Date() Shows the Wrong Time
Swift Protocol Extension Method Is Called Instead of Method Implemented in Subclass