I have a swiftui project. I am using pageview. Normally, when it comes to the last page, it does not scroll further because the pages are finished. What I want is this: the pages continue after reaching the last page. Let's start again from the first page. so I want my page order to be like this:
page 1 -> page 2 -> page 3 -> page 1 -> page 2 -> page 3 -> page 1 .....
I want it to repeat itself like this all the time.
struct PageControllerView: View {
var body: some View {
TabView {
Text("page 1")
Text("page 2")
Text("page 3")
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
Well you need to use the TabView with a selection:
struct PageControllerView: View {
#State var selection = 1
var body: some View {
TabView(selection: $selection) {
Text("page 3").tag(0) //same as last view for swipe effect
Text("page 1").tag(1)
Text("page 2").tag(2)
Text("page 3").tag(3)
Text("page 1").tag(4) //same as first view for swipe effect
}//.tabViewStyle(PageTabViewStyle()) BTW, You don't need that
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.onChange(of: selection) { newValue in
if selection == 4 { //last tag + 1
selection = 1 //first tag
}else if selection == 0 { //first tag - 1
selection = 3 //last tag
}
}
}
}
This also works with ForEach, but don't forget to choose a suitable tag.
Related
So I have a TabView where each of the tabs is embedded in a NavigationView.
On first appear of each tab I get the following lifecycle calls onAppear(), onDisappear(), onAppear(). So it looks like onAppear gets called twice. This only happens the first time. If I navigate back to the same tab, only onAppear() gets called, and only once.
Here's a minimal example:
struct Page1: View {
init() { print("Page 1 init") }
var body: some View {
NavigationView {
Text("Page 1")
.onAppear(perform: { print("Page 1 appearing") })
.onDisappear(perform: { print("Page 1 disappearing") })
}
}
}
struct Page2: View {
init() { print("Page 2 init") }
var body: some View {
NavigationView {
Text("Page 2")
.onAppear(perform: { print("Page 2 appearing") })
.onDisappear(perform: { print("Page 2 disappearing") })
}
}
}
struct ContentView: View {
var body: some View {
TabView {
Page1().tabItem { Text("Page 1") }
Page2().tabItem { Text("Page 2") }
}
}
}
And here's the result printed out:
Page 1 init
Page 2 init
Page 1 appearing
Page 1 disappearing
Page 1 appearing
Here's what happens if I click on the second tab
Page 1 init
Page 2 init
Page 1 appearing
Page 1 disappearing
Page 1 appearing
// here I clicked on second tab
Page 2 appearing
Page 2 disappearing
Page 2 appearing
Page 1 disappearing
TabView {
NavigationView {
VStack {
Color.red
.onAppear {
print("appear : red")
}
.onDisappear {
print("disappear : red")
}
}.onAppear {
print("appear")
}
}
}
Test on iOS 15 beta Simulator
The output:
appear : red
appear
disappear : red
appear : red
I have a tabview with two tabs (tabs A and B).
Clicking tab A opens a master View. In that master view there is a navigation link to Page 1. Within Page 1 there is also a link to Page 2.
When the user is on Page 1 or 2, and I tap Tab A, it doesn’t revert to master View. Similarly if the user clicks Tab B and then Tab A again, it returns to Page 1 or 2 (whichever the user was on), rather than master View.
How to I make the navigation stack reset in both cases?
Thanks!
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)
}
}
}
You can create RootView with MainView
import SwiftUI
struct RootView: View {
#ObservedObject var viewModel = RootViewModel()
init(){
viewModel.prepare()
}
var body: some View {
MainView(tab: viewModel.mainTab)
.id(UUID().uuidString)
}
}
Create RootViewModel with listeners to screen updating
import SwiftUI
class RootViewModel: ObservableObject{
#Published var mainTab: SelectedTab = .firstTab
let mainScreenNotification = NSNotification.Name("mainScreenNotification")
private var observerMain: Any?
func prepare(){
observerMain = NotificationCenter.default.addObserver(forName: mainScreenNotification, object: nil, queue: nil, using: { [unowned self] notification in
self.mainTab = (notification.userInfo?["selectedTab"])! as! SelectedTab
})
}
}
enum SelectedTab {
case firstTab, secondTab
}
Run this to inflating new tab screen from tab child:
NotificationCenter.default.post(name:
NSNotification.Name("mainScreenNotification"),
object: nil,
userInfo: ["selectedTab": SelectedTab.firstTab]
)
I am using down simple code for tabView. when I change the orientation of simulator, the app tab jump one tab back. like this if you are in page 2 goes to 1, but it must stays at page 2 how ever I changed the orientation. or if you are in page 3 jump to page 2. why this unwanted jump happens?
struct ContentView: View {
#State var currentPage: Int = 1
var body: some View {
ZStack
{
Color.gray.ignoresSafeArea()
TabView(selection: $currentPage)
{
Text("Hi 1").tag(0)
Text("Hi 2").tag(1)
Text("Hi 3").tag(2)
}
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .never))
.tabViewStyle(PageTabViewStyle())
}
}
}
I came across a weird Issue in SwiftUI.
I created a simple View that only holds a Button
and a TabView that uses the PageViewStyle. It seems that the TabView does not update it's content
correctly depending on the State of the Variable.
It seems that the content gets updated somehow but the View wont be updated how I would expect
Here is the Code of my View:
struct ContentView: View {
#State var numberOfPages: Int = 0
#State var selectedIndex = 0
var body: some View {
VStack {
Text("Tap Me").onTapGesture(count: 1, perform: {
self.numberOfPages = [2,5,10,15].randomElement()!
self.selectedIndex = 0
})
TabView(selection: $selectedIndex){
ForEach(0..<numberOfPages, id: \.self) { index in
Text("\(index)").background(Color.red)
}
}
.frame(height: 300)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .automatic))
}.background(Color.blue)
}
}
This is how the result looks after tapping the label several Times.
The Initial State is no 0 Pages. After you tap i would expect that the content of the
TabView changes so all Pages will be scrollable and visible but just the page indicator updates it State for some reason.
TabView expects to have container of pages, but you included only one HStack (with own dynamic content), moreover chaining number of pages you have to reset tab view, so here is a fix.
Tested with Xcode 12 / iOS 14
struct ContentView: View {
#State var numberOfPages: Int = 0
var body: some View {
VStack {
Text("Tap Me").onTapGesture(count: 1, perform: {
self.numberOfPages = [2,5,10,15].randomElement()!
})
if self.numberOfPages != 0 {
TabView {
ForEach(0..<numberOfPages, id: \.self) { index in
Text("\(index)").frame(width: 300).background(Color.red)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .automatic))
.frame(height: 300)
.id(numberOfPages) // << here !!
}
}
}
}
I try to create a splitview in SwiftUI, and I have 2 issues
1. the Detail-View not showing in the navigation
2. if I go to Detail-View from master view it shows in navigation (Navigation top bar), but when it goes to next page, back button not work
struct MainView: View {
var body: some View {
NavigationView {
ListView()
DetailView()
}
}
}
struct ListView: View {
let menuItems = [MenuItem(name: "Login"),
MenuItem(name: "KS & Token"),
MenuItem(name: "Household & Domain"),
MenuItem(name: "Register")]
var body: some View {
VStack{
List{
ForEach(self.menuItems, id:\.id) { item in
NavigationLink(destination: LoginView(menuItem: item)){
Text(item.name)
}
}
}
Spacer()
}
.navigationBarTitle(Text("User Menu"))
}
}
the issue is:
1. DetailView() not showing Navigation top bar
2. in LoginView, when I go to next page with NavigationLink, the app does not show back button, and also add it by code, not help