7

So I want to implement a custom control as a UIViewRepresentable which correctly handles focus using an @FocusState binding.

So I want to be able to manage the focus like so:

struct MyControl: UIViewRepresentable { ... }


struct Container: View {

    @FocusState var hasFocus: Bool = false

    var body: some View {
        VStack {
            MyControl()
                .focused($hasFocus)

            Button("Focus my control") {
                hasFocus = true
            }
        }
    }
}

What do I have to implement in MyControl to have it respond to the focus state properly? Is there a protocol or something which must be implemented?

sak
  • 2,612
  • 24
  • 55
  • Focus mostly make sense when we are working with Textfeld or a controller that use input from user to work, in your question your custom view makes no sense to have focus. Unless I understood it wrong, what you are looking from focus? – ios coder Apr 18 '22 at 11:08
  • 2
    My custom view is a custom control which would respond to user input. This would wrap a UITextField so I want to integrate the focus system from UIKit to the SwiftUI focus handling. – sak Apr 18 '22 at 11:11

1 Answers1

0

Disclaimer: the solution is not suitable for full custom controls. For these cases, you can try to pass the FocusState as binding: let isFieldInFocus: FocusState<Int?>.Binding

In my case, I have wrapped UITextView. In order to set the focus, I only used .focused($isFieldInFocus). Some of the information can be obtained through the property wrapper(@Environment(\.)), but this trick does not work with focus.

struct ContentView: View {
    @FocusState var isFieldInFocus: Bool
    @State var text = "Test message"
    @State var isDisabled = false
    
    var body: some View {
        VStack {
            Text("Focus for UITextView")
                .font(.headline)

            AppTextView(text: $text)
                .focused($isFieldInFocus)
                .disabled(isDisabled)
                .frame(height: 200)
            
            HStack {
                Button("Focus") {
                    isFieldInFocus = true
                }
                .buttonStyle(.borderedProminent)
                Button("Enable/Disable") {
                    isDisabled.toggle()
                }
                .buttonStyle(.borderedProminent)
            }
        }
        .padding()
        .background(.gray)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct AppTextView: UIViewRepresentable {
    @Binding var text: String
    @Environment(\.isEnabled) var isEnabled: Bool
    @Environment(\.isFocused) var isFocused: Bool // doesn't work
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.font = UIFont.preferredFont(forTextStyle: .body)
        textView.delegate = context.coordinator
        
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
        print("isEnabled", isEnabled, "isFocused", isFocused)
    }
    
    class Coordinator : NSObject, UITextViewDelegate {
        private var parent: AppTextView
        
        init(_ textView: AppTextView) {
            self.parent = textView
        }
        
        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text
        }
    }
}
Alexander
  • 244
  • 3
  • 8