Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI TextField acts disabled in VStack inside ZStack (simulating an Alert with a TextField)

I need to make an Alert in SwiftUI that has an editable TextField in it. Currently, this isn't supported by SwiftUI (as of Xcode 11.3), so I'm looking for a work-around.

I know I can implement by wrapping the normal UIKit bits in a UIHostingController, but really want to stick with an all-SwiftUI implementation.

I've got two VStacks in a ZStack, with the front one (the one with the TextView) being hidden and disabled until until you tap the button. Take a look at this:

import SwiftUI

struct ContentView: View {
    @State var isShowingEditField = false
    @State var text: String = "12345"

    var body: some View {
        ZStack {
            VStack {
                Text("Value is \(self.text)")
                Button(action: {
                    print("button")
                    self.isShowingEditField = true
                }) {
                    Text("Tap To Test")
                }
            }
            .disabled(self.isShowingEditField)
            .opacity(self.isShowingEditField ? 0.25 : 1.00)

            VStack(alignment: .center) {
                Text("Edit the text")
                TextField("", text: self.$text)
                    .multilineTextAlignment(.center)
                    .lineLimit(1)

                Divider()
                HStack {
                    Button(action: {
                        withAnimation {
                            self.isShowingEditField = false
                            print("completed... value is \(self.text)")
                        }
                    }) {
                        Text("OK")
                    }
                }
            }
            .padding()
            .background(Color.white)
            .shadow(radius: CGFloat(1.0))
            .disabled(!self.isShowingEditField)
            .opacity(self.isShowingEditField ? 1.0 : 0.0)
        }
    }
}

This seems like it should work to me. Switching between the two VStacks works well, but the TextField is not editable.

It acts like it's disabled, but it's not. Explicitly .disabled(false) to the TextField doesn't help. Also, it should already be enabled anyway since 1) that's the default, 2) the VStack it's in is specifically being set as enabled, and 3) The OK button works normally.

Ideas/workarounds?
Thanks!

like image 772
drewster Avatar asked Dec 26 '19 21:12

drewster


2 Answers

You need to force the TextField updating with some methods. Like the following:

    TextField("", text: self.$text)
                .multilineTextAlignment(.center)
                .lineLimit(1)
                .id(self.isShowingEditField)
like image 179
E.Coms Avatar answered Dec 14 '22 22:12

E.Coms


That is really ridiculous, but the problem disappears if you comment just one line of code:

//.shadow(radius: CGFloat(1.0))

I commented it and everything works. I think you need to use ZStack somewhere in your custom alert view to avoid this.. hm, bug, maybe?

update try some experiments. If you leave just this code in that View:

struct CustomAlertWithTextField: View {

    @State var isShowingEditField = false
    @State var text: String = "12345"

    var body: some View {
        TextField("", text: $text)
            .padding()
            .shadow(radius: CGFloat(1.0))
    }
}

it would not work again. only if you comment .padding() or .shadow(...)

BUT if you relaunch Xcode - this code begin to work (which made me crazy). Tried it at Xcode Version 11.2 (11B52)

update 2 the working code version:

struct CustomAlertWithTextField: View {

    @State var isShowingEditField = false
    @State var text: String = "12345"

    var body: some View {
        ZStack {
            VStack {
                Text("Value is \(self.text)")
                Button(action: {
                    print("button")
                    self.isShowingEditField = true
                }) {
                    Text("Tap To Test")
                }
            }
            .disabled(self.isShowingEditField)
            .opacity(self.isShowingEditField ? 0.25 : 1.00)

            ZStack {
                Rectangle()
                    .fill(Color.white)
                    .frame(width: 300, height: 100)
                    .shadow(radius: 1) // moved shadow here, so it doesn't affect TextField now

                VStack(alignment: .center) {
                    Text("Edit the text")
                    TextField("", text: self.$text)
                        .multilineTextAlignment(.center)
                        .lineLimit(1)
                        .frame(width: 298)

                    Divider().frame(width: 300)

                    HStack {
                        Button(action: {
                            withAnimation {
                                self.isShowingEditField = false
                                print("completed... value is \(self.text)")
                            }
                        }) {
                            Text("OK")
                        }
                    }
                }
            }
            .padding()
            .background(Color.white)
            .disabled(!self.isShowingEditField)
            .opacity(self.isShowingEditField ? 1.0 : 0.0)
        }
    }

}
like image 44
Hrabovskyi Oleksandr Avatar answered Dec 14 '22 23:12

Hrabovskyi Oleksandr