Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI PageTabView in iOS14.2 will Recall ChildView onAppear method Many times

I use TabView PageTabViewStyle with SwiftUI to display a pageview, when I swipe this TabView I find child view will Recall onAppear method Many times, Can someone tell me why?

This is my code

import SwiftUI

struct Pageview: View {
    
    @StateObject var vm = PageViewModel()
    
    var body: some View {
        VStack {
            
            DragViewBar().padding(.top, 14)
            
            TabView(selection: $vm.selectTabIndex) {
                
                TextView(index: "0").tag(0)
                TextView(index: "1").tag(1)
                TextView(index: "2").tag(2)
                TextView(index: "3").tag(3)
                TextView(index: "4").tag(4)
                TextView(index: "5").tag(5)
                
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            
        }

    }
}

struct TextView: View {
    
    let index: String
    
    var body: some View {
        VStack {
            Text(index)
        }
        .onAppear { print(index) }
        
    }
}

struct DragViewBar: View {
    var body: some View {
        Rectangle()
            .frame(width:36.0,height:5.0).foregroundColor(Color.blue)
            .cornerRadius(100)
    }
}

class PageViewModel: ObservableObject {
    @Published var selectTabIndex = 0
}

The result of the console printing enter image description here

The correct case is to print only once per swipe

It just has a problem in ios14.2, 14.1 will be ok, you can load my code in Github: https://github.com/werbhelius/TabViewBug

Xcode version: 12.1 (12A7403)

Device: iPhone 6s iOS 14.2

I think you can reproduce this problem on any device in iOS 14.2

I look forward to your help to solve this problem. Thank you

like image 604
wanbo Avatar asked Nov 02 '20 08:11

wanbo


2 Answers

Views are preloaded at the discretion of SwiftUI. Sometimes more than others depending on the device's available resources. onAppear is called even if it has appeared out of view (pre-loaded)

import SwiftUI

struct PageView: View {
    
    @StateObject var vm = PageViewModel()
    
    var body: some View {
        VStack {
            
            DragViewBar().padding(.top, 14)
            
            TabView(selection: $vm.selectTabIndex) {
                
                TextView(index: "0").tag(0)
                TextView(index: "1").tag(1)
                TextView(index: "2").tag(2)
                TextView(index: "3").tag(3)
                TextView(index: "4").tag(4)
                TextView(index: "5").tag(5)
                
            }
            //This lets you perform an operation when the value has changed
            .onReceive(vm.$selectTabIndex, perform: { idx in
                print("PageView :: body :: onReceive" + idx.description)
            })
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            
        }

    }
}

struct TextView: View {
    
    let index: String
    
    var body: some View {
        VStack {
            Text(index)
        }
        //Views are pre-loaded at the discretion of SwiftUI
        .onAppear { print(index) }
        
        .onReceive(index.publisher, perform: { idx in
            print("TextView :: body :: onReceive" + idx.description)
        })
        
    }
}

struct DragViewBar: View {
    var body: some View {
        Rectangle()
            .frame(width:36.0,height:5.0).foregroundColor(Color.blue)
            .cornerRadius(100)
    }
}

class PageViewModel: ObservableObject {
    @Published var selectTabIndex = 0
}


struct PageView_Previews: PreviewProvider {
    static var previews: some View {
        PageView()
    }
} 
like image 159
lorem ipsum Avatar answered Nov 19 '22 15:11

lorem ipsum


For everyone who is still struggling to find a good workaround for this problem. I've managed to use this framework https://github.com/fermoya/SwiftUIPager which can be used to mimic the TabView .tabViewStyle(PageTabViewStyle()) style.

import SwiftUIPager
struct Dummy: View {
    @StateObject var page: Page = .first()
    let numberOfPages : Int = 3
    var body: some View {
        
        Pager(page: self.page,
              data: Array(0..<numberOfPages),
              id: \.self,
              content: { index in
                
                switch (index) {
                case 0:
                    ZStack{
                        Color.white
                        Text("Test")
                    }
                case 1:
                    ZStack{
                        Color.white
                        Text("Test2")
                    }
                default:
                    ZStack{
                        Color.white
                        Text("Test3")
                    }
                }
                
              }
        )
        .pagingPriority(.simultaneous)
        .onPageWillChange { (newIndex) in
            print("Page \(self.page.index) will change to \(newIndex) i.e. onDisappear")
            switch (self.page.index) {
            case 0:
                print("onDisappear of \(self.page.index)")
            case 1:
                print("onDisappear of \(self.page.index)")
            default:
                print("onDisappear of \(self.page.index)")
            }
        }
        .onPageChanged { (newIndex) in
            print("Has changed to page \(self.page.index) i.e. onAppear")
            switch (newIndex) {
            case 0:
                print("onAppear of \(newIndex)")
            case 1:
                print("onAppear of \(newIndex)")
            default:
                print("onAppear of \(newIndex)")
            }
        }
        
    }
}
like image 3
FriendlyGuy Avatar answered Nov 19 '22 14:11

FriendlyGuy