繁体   English   中英

SwiftUI PageTabView 在 iOS14.2 中会多次调用 ChildView onAppear 方法

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

我使用带有 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 中的任何设备上重现此问题

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

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

对于仍在努力寻找解决此问题的好方法的每个人。 我已经设法使用这个框架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)")
            }
        }
        
    }
}

由于各种 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
        }
      })
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM