Home > Mobile >  Presence of @Environment dismiss causes list to constantly rebuild its content on scrolling
Presence of @Environment dismiss causes list to constantly rebuild its content on scrolling

Time:07-10

I need to build a list of TextFields where each field is associated with focus id, so that I can auto scroll to such a text field when it receives focus. In reality the real app is a bit more complex which also includes TextEditors and many other controls.

Now, I found out that if my view defines @Environment(\.dismiss) private var dismiss then the list is rebuilding all the time during manual scrolling. If I just comment out the line @Environment(\.dismiss) private var dismiss then there is no rebuilding of the list when I scroll. Obviously, I want to be able to dismiss my view when user clicks some button. In the real app it's even worse: during scrolling everything is lagging, I cannot get smooth scrolling. And my list is not huge it's just 10 items or so.

Here is a demo example:

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink {
                DismissListView()
            } label: {
                Text("Go to see the list")
            }
        }
    }
}

struct DismissListView: View {
    @Environment(\.dismiss) private var dismiss
    
    enum Field: Hashable {
        case line(Int)
    }
    
    @FocusState private var focus: Field?
    @State private var text: String = ""
    
    var body: some View {
        ScrollViewReader { proxy in
            List {
                let _ = print("body is rebuilding")
                
                Button("Dismiss me") {
                    dismiss()
                }
                
                Section("Section") {
                    ForEach((1...100), id: \.self) {num in
                        TextField("text", text: $text)
                            .id(Field.line(num))
                            .focused($focus, equals: .line(num))
                    }
                }
            }
            .listStyle(.insetGrouped)
            .onChange(of: focus) {_ in
                withAnimation {
                    proxy.scrollTo(focus, anchor: .center)
                }
            }
        }
    }
}

The questions are:

  • Why is the list rebuilding during manual back and forth scrolling when @Environment(\.dismiss) private var dismiss is defined, and the same is NOT happening when dismiss is NOT defined?
  • Is there any workaround for this: I need to be able to use ScrollProxyReader to focus any text field when the focus changes, and I need to be able to dismiss the view, but in the same time I need to avoid constant rebuilds of the list during scrolling, because it drops app performance and scrolling becomes jagged...

P.S. Demo app constantly outputs "body is rebuilding" when dismiss is defined and the list is scrolled, but if any text field gets a focus manually, then the "body is rebuilding" is not printed anymore even if the dismiss is still defined.

CodePudding user response:

  1. I could make an assumption, but that would be really rather a guess (based on experience, observations, etc). In a fact, all WHYs like "why this sh... (bug) happens" should be asked on https://developer.apple.com/forums/ (there are Apple's engineers there) or reported to https://developer.apple.com/bug-reporting/

  2. A solution is to separate dismiss depenent part into dedicated view, so hiding it from parent body (and so do not affect it)

    struct DismissView: View {
        // visible only for this view !!
        @Environment(\.dismiss) private var dismiss

        var body: some View {
            Button("Dismiss me") {
                // affects current context, so it does not matter
                // in which sub-view is called
                dismiss()           
            }
        }
    }

    var body: some View {
        ScrollViewReader { proxy in
            List {
                let _ = print("body is rebuilding")
                DismissView()   // << here !!

    // ... other code

  • Related