Home > other >  App freeze when tap on textfield in conditional section
App freeze when tap on textfield in conditional section

Time:03-04

I have a textfield that can be modified programatically. It works fine, until the form section is conditionally displayed, in that case the app freezes when I use the textfield.

Here is my code (I removed unnecessary stuff):

struct test: View {
    @Environment(\.presentationMode) var presentationMode
    @State private var displaySection = true
    @State private var amount: Double?
    @FocusState private var isFocused: Bool
    
    var body: some View {
        NavigationView{
            Form {
                if displaySection {     // <-- works well without that condition, otherwise app freezes
                    Section {
                        VStack {
                            HStack {
                                Text("Amount EUR")
                                Spacer()
                                TextField("Type amount", value: $amount, format: .number)
                                    .keyboardType(.numberPad)
                                    .multilineTextAlignment(.trailing)
                                    .focused($isFocused)
                            }
                            Text("Set MAX (999)")
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .onTapGesture {
                                    isFocused = false
                                    amount = 999
                                }
                        }
                    }
                }
            }
        }
    }
}

I need to have some sections hidden in the form so I'm stuck on that, didn't succeed to skirt this issue. Problem appears on simulator as well on device.

Is this behavior normal, or is there a possible workaround ? Thanks for your help :)

CodePudding user response:

Divide and conquer.

You presented a really weird behaviour in your app, I would never imagine such bug. I tested your code, it actually freezes the app.

Now, I tested the solution below: move your Section to a separate view. In my Xcode, it works.

struct Example: View {
    @Environment(\.presentationMode) var presentationMode
    @State private var displaySection = true
    
    var body: some View {
        NavigationView{
            Form {
                if displaySection {     // <-- works well ALSO WITH that condition
                    ConditionalSection()
                }
                
                Button {
                    withAnimation {
                        displaySection.toggle()
                    }
                } label: {
                    Text("Display or not display?")
                }
            }
        }
    }
}

struct ConditionalSection: View {
    @State private var amount: Double?
    @FocusState private var isFocused: Bool
    
    var body: some View {
        Section {
            VStack {
                HStack {
                    Text("Amount EUR")
                    Spacer()
                    TextField("Type amount", value: $amount, format: .number)
                        .keyboardType(.numberPad)
                        .multilineTextAlignment(.trailing)
                        .focused($isFocused)
                }
                Text("Set MAX (999)")
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .onTapGesture {
                        isFocused = false
                        amount = 999
                    }
            }
        }

    }
}

CodePudding user response:

So with Xcode 13.2.1 on Intel and iOS 15.2 I can only get this example code to lock-up in the simulator and on a real device if I type a number of more than three digits in the 'Amount EUR' and then attempt to 'Set the Max to 999' i.e. a three digit number.

In previous versions of SwiftUI the only input binding TextField would work with was String. It seems likely that although the current version accepts binding to numerics the way it is doing this is to map, and the underlying mechanism remains that of the original String based TextField (typing in the TextField with an attached keyboard still unfortunately renders non-numeric characters regardless of keyboardType as it did the last time I experimented https://gist.github.com/shufflingB/23daafa5253c3355cdf18934599cd54c)

For whatever reason this new(ish) mapping process appears to be getting confused when more than three digits are entered and the TextField is then by default automatically adding a number grouping character.

Two work-arounds seem to address the problem:

  1. Push the change to amount triggered by Set the Max ... to a later state update cycle using DispatchQueue.main.asyncAfter

  2. Give an option to remove the troublesome grouping character from the TextField.

struct ContentView: View {
    @Environment(\.presentationMode) var presentationMode
    @State private var displaySection = true
    @State private var amount: Double?
    @FocusState private var isFocused: Bool

    var body: some View {
        NavigationView {
            Form {
                if displaySection {
                    Section {
                        VStack {
                            HStack {
                                Text("Amount EUR")
                                Spacer()
                                TextField("Type amount", value: $amount, format: .number)
                                    .keyboardType(.numberPad)
                                    .multilineTextAlignment(.trailing)
                                    .focused($isFocused)

                                /* Alternative to Dispatch, remove number grouping
                                TextField("Type amount", value: $amount, format: .number.grouping(.never))
                                    .keyboardType(.numberPad)
                                    .multilineTextAlignment(.trailing)
                                    .focused($isFocused)
                                 */
                            }
                            Text("Set MAX (999)")
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .onTapGesture {
                                    isFocused = false
                                    DispatchQueue.main.asyncAfter(deadline: .now()   0.1) { // <- Push change to subsequent update cycle
                                        amount = 999
                                    }
                                }
                        }
                    }
                }
            }
        }
    }
}

  • Related