如何使SwiftUI忘记内部状态

时间:2020-05-09 04:06:02

标签: swiftui

我有以下观点

struct A: View {
    var content: AnyView
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            VStack {
                // Common Elements
                content
                // More Common Elements
            }
        }
    }
}

当我从另一个视图调用此视图时

A(nextInnerView())

发生两件事。首先,随着content元素的大小更改,ScrollView会为过渡设置动画效果。其次,如果向下滚动然后更改content,则滚动位置不会重置。

1 个答案:

答案 0 :(得分:1)

这里是可能解决方案的演示。使用Xcode 11.4 / iOS 13.4进行了测试

此行为的起源是在SwiftUI渲染优化中,它试图重新渲染仅更改的部分,因此,方法是根据源自采访更改的条件来识别视图A(将其标记为完全更改),或者可以仅通过UUID()进行标识。

demo

struct TestInnerViewReplacement: View {
    @State private var counter = 0
    var body: some View {
        VStack {
            Button("Next") { self.counter += 1 }
            Divider()
            A(content: nextInnerView())
               .id(counter)              // << here !!
        }
    }

    private func nextInnerView() -> AnyView {
        AnyView(Group {
            if counter % 2 == 0 {
                Text("Text Demo")
            } else {
                Image(systemName: "star")
                    .resizable()
                    .frame(width: 100, height: 100)
            }
        })
    }
}

struct A: View {
    var content: AnyView

    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            VStack {
                ForEach(0..<5) { _ in      // upper content demo 
                    Rectangle().fill(Color.yellow)
                        .frame(height: 40)
                        .frame(maxWidth: .infinity)
                        .padding()
                }

                content

                ForEach(0..<10) { _ in    // lower content demo
                    Rectangle().fill(Color.blue)
                        .frame(height: 40)
                        .frame(maxWidth: .infinity)
                        .padding()
                }
            }
        }
    }
}