Skip to content

Any Reader views doesn't update content. #10

Description

@wlsdms0122

Problem

It seems to be a SwiftUI bug. All Reader type views (such as GeometryReader or ScrollViewReader) don't update their content even if state changes.

This is the code that reproduces the behavior.

class MainViewController: ComposableController {
    final class Environment: ObservableObject {
        @Published
        var show: Bool = false
    }
    
    // MARK: - Property
    
    // MARK: - Initializer
    init() {
        let env = Environment()
        super.init(env)
        
        run {
            VStack {
                GeometryReader { _ in
                    // Not updated
                    Text(env.show ? "A" : "B")
                }
                
                // Updated
                Text(env.show ? "AA" : "BB")
                Button("toggle") {
                    env.show.toggle()
                }
            }
        }
    }
    ...
}

Pure SwiftUI version

class ViewModel: ObservableObject {
    @Published
    var show: Bool = false
}

struct Parent: View {
    let viewModel = ViewModel()
    
    var body: some View {
        Child(viewModel) {
            VStack {
                GeometryReader { _ in
                    // Not updated
                    Text(viewModel.show ? "A" : "B")
                }
                
                // Updated
                Text(viewModel.show ? "AA" : "BB")
                Button("toggle") {
                    viewModel.show.toggle()
                }
            }
        }
    }
}

struct Child<Content: View>: View {
    var body: some View {
        content()
    }
    
    @StateObject
    var viewModel: ViewModel
    let content: () -> Content
    
    init(_ viewModel: ViewModel, content: @escaping () -> Content) {
        self._viewModel = .init(wrappedValue: viewModel)
        self.content = content
    }
}

There seems to be a problem with the state capture system for rendering. When the content closure captures a StateObject, the content closure of the reader view isn't capturing the state correctly.

Workaround

Make as sub view that include reader view.

AS-IS

VStack {
    GeometryReader { _ in
        Text(env.show ? "A" : "B")
    }
    
    Text(env.show ? "AA" : "BB")
    Button("toggle") {
        env.show.toggle()
    }
}

TO-BE

VStack {
    SubView(show: .init(
        get: { env.show },
        set: { env.show = $0 }
    ))
    
    Text(env.show ? "AA" : "BB")
    Button("toggle") {
        env.show.toggle()
    }
}

/// Or make as `struct`.
@ViewBuilder
func SubView(show: Binding<Bool>) -> some View {
    GeometryReader { _ in
        Text(show.wrappedValue ? "A" : "B")
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions