vasily
vasily

Reputation: 2920

How to make SwiftUI to forget internal state

I have a view like following

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

When I call this from another view like

A(nextInnerView())

two things happen. Firstly, as the size of the content element changes ScrollView animates the transition. Secondly, if you scroll down and then change the content the scrolling position does not reset.

Upvotes: 0

Views: 30

Answers (1)

Asperi
Asperi

Reputation: 257701

Here is a demo of possible solution. Tested with Xcode 11.4 / iOS 13.4

The origin of this behaviour is in SwiftUI rendering optimisation, that tries to re-render only changed part, so approach is to identify view A (to mark it as completely changed) based on condition that originated in interview changes, alternatively it can be identified just by 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()
                }
            }
        }
    }
}

Upvotes: 1

Related Questions