简体   繁体   English

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

[英]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?我使用带有 SwiftUI 的 TabView PageTabViewStyle 来显示页面视图,当我滑动这个 TabView 时,我发现子视图会多次调用 onAppear 方法,有人能告诉我为什么吗?

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控制台打印结果在此处输入图片说明

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只是ios14.2有问题,14.1就可以了,你可以在Github加载我的代码: https : //github.com/werbhelius/TabViewBug

Xcode version: 12.1 (12A7403) Xcode 版本:12.1 (12A7403)

Device: iPhone 6s iOS 14.2设备:iPhone 6s iOS 14.2

I think you can reproduce this problem on any device in iOS 14.2我认为您可以在 iOS 14.2 中的任何设备上重现此问题

I look forward to your help to solve this problem.我期待着您的帮助来解决这个问题。 Thank you谢谢

View s are preloaded at the discretion of SwiftUI. View由 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)即使出现在视野之外(预加载),也会调用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()
    }
} 

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.我已经设法使用这个框架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)")
            }
        }
        
    }
}

I've encountered similar problems due to bugs/features which have appeared with various 14.x releases.由于各种 14.x 版本中出现的错误/功能,我遇到了类似的问题。 For example, on iOS 14.3 the code above prints this to the console at launch:例如,在 iOS 14.3 上,上面的代码在启动时将其打印到控制台:

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

and this when swiping to index "1":当滑动到索引“1”时:

TextView :: body :: onReceive1 1 TextView :: body :: onReceive2 2 PageView :: body :: onReceive1 1 1 1 1 1 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. .onAppear/.onDisappear 可能被调用一次、多次或根本不调用,因此不能使用。
  2. Other modifiers like .onReceive and .onChange may be called repeatedly and therefore require debouncing.其他修饰符如 .onReceive 和 .onChange 可能会被重复调用,因此需要去抖动。

Here's a simplified example of a workaround that allows detection and debouncing of TabView page changes without using .onAppear.这是一个简化的解决方法示例,它允许在不使用 .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