我正在使用 SwiftUI 为我的 iOS 应用程序开发入门流程。我遇到一个问题,多次调用 OnboardingPage 结构的 init 方法,导致我的应用程序出现意外行为。
这是我的代码结构:
struct Onboarding: View {
// MARK: - Properties
@State private var selected: Int = 0
private var pages: [OnboardingPage.Page] = [
.init(
id: 1,
image: #IMAGE_ONE,
title: "#TITLE",
description: "#DESCRIPTION"
),
.init(
id: 2,
image: #IMAGE_TWO,
title: "#TITLE",
description: "#DESCRIPTION"
),
.init(
id: 3,
image: #IMAGE_THREE,
title: "#TITLE",
description: "#DESCRIPTION"
),
.init(
id: 4,
image: #IMAGE_FOUR,
title: "#TITLE",
description: "#DESCRIPTION"
)
]
// MARK: - Body
var body: some View {
TabView(selection: $selected) {
ForEach(pages) { page in
OnboardingPage(page: page)
.tag(page.id)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
.animation(.easeInOut, value: selected)
.ignoresSafeArea()
.overlay(alignment: .top) {
OnboardingPageIndicator(selected: $selected, total: 4)
.padding(.horizontal, 16)
.padding(.vertical, 16)
}
.overlay(alignment: .topTrailing) {
Button("Skip") {
print("SKIP")
}
.font(.headline)
.foregroundStyle(Color.appSecondary)
.padding(.horizontal, 16)
.padding(.vertical, 8)
}
.overlay(alignment: .bottom) {
Button {
guard selected < pages.count - 1 else { return }
selected += 1
} label: {
Image(systemName: "arrow.right.circle.fill")
.resizable()
.frame(width: 45, height: 45)
.foregroundColor(.appSecondary)
.padding()
}
.buttonStyle(AppButtonStyle())
.padding(.bottom)
}
.onAppear {
UIScrollView.appearance().bounces = false
}
}}
OnboardingPage View 代码
struct OnboardingPage: View {
// MARK: - Properties
struct Page: Identifiable {
var id: Int
let image: ImageResource
let title: String
let description: String
}
let page: Page
init(page: Page) {
self.page = page
print(page.id) // This print statement shows the init method being called multiple times
}
// MARK: - Body
var body: some View {
ZStack {
Image(page.image)
.resizable()
.ignoresSafeArea()
VStack(spacing: 12) {
Text(page.title.uppercased())
.font(.header)
.kerning(1)
.lineSpacing(8)
.foregroundStyle(Color.appSecondary)
Text(page.description)
.font(.headline1)
.lineSpacing(1)
.foregroundStyle(Color.appPrimary)
.padding(.horizontal)
Spacer()
}
.multilineTextAlignment(.center)
.padding(.horizontal, 24)
.padding(.top, 50)
}
}}
OnboardingPageIndicator 视图代码
struct OnboardingPageIndicator: View {
// MARK: - Properties
@Namespace var namespace
@Binding var selected: Int
let total: Int
let unSelectedTintColor: Color = .gray
let selectedTintColor: Color = .appPrimary
// MARK: - Body
var body: some View {
HStack(spacing: 8) {
ForEach(0..<total, id: \.self) { page in
Circle()
.fill(page == selected ? selectedTintColor : unSelectedTintColor)
.frame(width: 8, height: 8)
.matchedGeometryEffect(id: page, in: namespace)
.animation(.easeInOut, value: selected)
}
}
}}
在入职视图中,我使用具有自定义页面样式的 TabView 来显示多个入职页面。每个页面都由 OnboardingPage 结构体的一个实例表示。
但是,我注意到 OnboardingPage 结构的 init 方法被多次调用,这似乎导致了我的应用程序中的性能问题和意外行为。
我已经检查了状态管理问题,并确保用于导航的所选状态变量得到正确管理。我还审查了我的视图层次结构并简化了我的视图以最大程度地减少不必要的工作。
任何有关为什么多次调用 init 方法以及如何解决此问题的见解将不胜感激。谢谢!
在 SwiftUI 中,由于框架的声明性性质以及 SwiftUI 管理视图层次结构和状态更新的方式,在某些情况下会多次调用视图的
init()
方法。因此,我建议使用 ViewModel 中的
init()
方法,因为在大多数情况下,这个 init()
通常只会被调用一次。init()
,您可以使用 isViewInitialized
类型创建类似 Bool
的属性,尽管我不推荐这种方法