SwfitUI 视图 `init` 被多次调用

问题描述 投票:0回答:1

我正在使用 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 方法以及如何解决此问题的见解将不胜感激。谢谢!

ios iphone swiftui ipad swiftui-tabview
1个回答
0
投票

在 SwiftUI 中,由于框架的声明性性质以及 SwiftUI 管理视图层次结构和状态更新的方式,在某些情况下会多次调用视图的

init()
方法。
即使视图的属性或状态没有显式更改,也会发生此行为。

因此,我建议使用 ViewModel 中的

init()
方法,因为在大多数情况下,这个
init()
通常只会被调用一次。
如果您仍然喜欢在视图中使用
init()
,您可以使用
isViewInitialized
类型创建类似
Bool
的属性,尽管我不推荐这种方法

© www.soinside.com 2019 - 2024. All rights reserved.