iOS14.2 SwiftUI PageTabView 会多次调用 ChildView onAppear 方法
Posted
技术标签:
【中文标题】iOS14.2 SwiftUI PageTabView 会多次调用 ChildView onAppear 方法【英文标题】:SwiftUI PageTabView in iOS14.2 will Recall ChildView onAppear method Many times 【发布时间】: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 的任何设备上重现此问题
我期待您的帮助来解决这个问题。谢谢
【问题讨论】:
【参考方案1】:View
s 由 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
不适用于您的用例***.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
似乎有两个问题:
-
.onAppear/.onDisappear 可能会被调用一次、多次或根本不调用,因此无法使用。
.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
)
【讨论】:
以上是关于iOS14.2 SwiftUI PageTabView 会多次调用 ChildView onAppear 方法的主要内容,如果未能解决你的问题,请参考以下文章
iOS 15.3.1中SwiftUI toolbar中按钮不响应点击动作等若干不兼容问题的解决
iOS 15.3.1中SwiftUI toolbar中按钮不响应点击动作等若干不兼容问题的解决