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

前端 未结 3 1684
轮回少年
轮回少年 2021-01-23 23:41

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?

3条回答
  •  遇见更好的自我
    2021-01-23 23:48

    I've encountered similar problems due to bugs/features which have appeared with various 14.x releases. For example, on iOS 14.3 the code above prints this to the console at launch:

    PageView :: body :: onReceive0 TextView :: body :: onReceive0 0 0 0 0 0 0 0 0 0 0 0

    and this when swiping to index "1":

    TextView :: body :: onReceive1 1 TextView :: body :: onReceive2 2 PageView :: body :: onReceive1 1 1 1 1 1 1

    There appear to be two issues:

    1. .onAppear/.onDisappear may be called once, multiple times or not at all and therefore can't be used.
    2. Other modifiers like .onReceive and .onChange may be called repeatedly and therefore require debouncing.

    Here's a simplified example of a workaround that allows detection and debouncing of TabView page changes without using .onAppear.

    import SwiftUI
    
    struct ContentView: View {
      @State private var currentView: Int = 0
      
      var body: some View {
        
        TabView(selection: $currentView) {
          ChildView1(currentView: $currentView).tag(1)
          ChildView2(currentView: $currentView).tag(2)
        }
        .tabViewStyle(PageTabViewStyle())
      }
    }
    
    struct ChildView1: View {
      @Binding var currentView: Int
      @State private var debouncer = 0
      let thisViewTag = 1
      
      var body: some View {
        Text("View 1")
          
          .onChange(of: currentView, perform: { value in
            if value == thisViewTag {
              debouncer += 1
              if debouncer == 1 {
                print ("view 1 appeared")
              }
            } else {
              debouncer = 0
            }
          })
      }
    }
    
    struct ChildView2: View {
      @Binding var currentView: Int
      @State private var debouncer = 0
      let thisViewTag = 2
      
      var body: some View {
        Text("View 2")
          
          .onChange(of: currentView, perform: { value in
            if value == thisViewTag {
              debouncer += 1
              if debouncer == 1 {
                print ("view 2 appeared")
              }
            } else {
              debouncer = 0
            }
          })
      }
    }
    

提交回复
热议问题