我在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中的任何设备上重现此问题
我们期待您的帮助解决此问题。谢谢
答案 0 :(得分:1)
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()
}
}
答案 1 :(得分:1)
对于仍在努力寻找解决此问题的好方法的每个人。我设法使用了这个框架 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)")
}
}
}
}
答案 2 :(得分:0)
由于各种 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 的情况下检测和消除 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
}
})
}
}