【问题标题】:SwiftUI PageTabView in iOS14.2 will Recall ChildView onAppear method Many timesiOS14.2 SwiftUI PageTabView 会多次调用 ChildView onAppear 方法
【发布时间】:2020-11-02 08:00:47
【问题描述】:

我在 SwiftUI 中使用 TabView PageTabViewStyle 来显示一个页面视图,当我滑动这个 TabView 时,我发现子视图会调用 onAppear 方法很多次,有人能告诉我为什么吗?

这是我的代码

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
}

控制台打印的结果

正确的情况是每次滑动只打印一次

只是ios14.2有问题,14.1就可以了,你可以在Github上加载我的代码:https://github.com/werbhelius/TabViewBug

Xcode 版本:12.1 (12A7403)

设备:iPhone 6s iOS 14.2

我认为您可以在 iOS 14.2 的任何设备上重现此问题

我期待您的帮助来解决这个问题。谢谢

【问题讨论】:

    标签: ios ios14 swiftui uitabview


    【解决方案1】:

    Views 由 SwiftUI 自行决定预加载。有时比其他更多取决于设备的可用资源。 onAppear 被调用,即使它已经出现在视野之外(预加载)

    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()
        }
    } 
    

    【讨论】:

    • 但是我只在 14.2 上重复调用 onAppear 才发现这个问题,这对于 14.1 版本来说是正常的,我怀疑这是 ios 14.2 上 TabView 中的一个错误
    • 我也有同样的问题!我的应用程序依赖于 PageTabViewStyle,因此它在 iOS 14.2 上完全崩溃了。|你找到解决办法了吗?
    • 我不确定,但PageTabViewStyle 可能只是出于“效率”目的而采用了List 的这种预加载行为。一种流行的解决方法是struct LazyView<Content: View>: View 我还没有为PageTabViewStyle 尝试过,但如果onReceive 不适用于您的用例stackoverflow.com/questions/58357414/…,那么这可能是值得的努力@
    • 你们有没有发现这个问题? @MaximeHeckel 我在 14.3 中仍然面临这个问题。
    • 上面的答案对我有用,这就是我最终要做的:twitter.com/MaximeHeckel/status/1328381063287042048/photo/2
    【解决方案2】:

    适合所有仍在努力寻找解决此问题的好方法的人。我已经设法使用了这个框架https://github.com/fermoya/SwiftUIPager,它可以用来模仿TabView .tabViewStyle(PageTabViewStyle()) 风格。

    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)")
                }
            }
            
        }
    }
    

    【讨论】:

      【解决方案3】:

      由于各种 14.x 版本中出现的错误/功能,我遇到了类似的问题。例如,在 iOS 14.3 上,上面的代码在启动时将其打印到控制台:

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

      当滑动到索引“1”时:

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

      似乎有两个问题:

      1. .onAppear/.onDisappear 可能会被调用一次、多次或根本不调用,因此无法使用。
      2. .onReceive 和 .onChange 等其他修饰符可能会重复调用,因此需要去抖动。

      这是一个解决方法的简化示例,它允许在不使用 .onAppear 的情况下检测和消除 TabView 页面更改。

      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
              }
            })
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-14
        • 1970-01-01
        • 2021-08-01
        • 2021-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多