J'ai pris une approche un peu différente - au lieu de UIViewRepresentable
sur la base de UITextField
Je l'ai fait en me basant sur UIView
et de l'insérer dans la hiérarchie des vues de SwiftUI avec background
modificateur. À l'intérieur de UIView
J'ai ajouté une logique pour trouver la première vue qui canBecomeFirstResponder
dans les sous-vues et les vues parent.
private struct FocusableUIView: UIViewRepresentable {
var isFirstResponder: Bool = false
class Coordinator: NSObject {
var didBecomeFirstResponder = false
}
func makeUIView(context: UIViewRepresentableContext<FocusableUIView>) -> UIView {
let view = UIView()
view.backgroundColor = .clear
return view
}
func makeCoordinator() -> FocusableUIView.Coordinator {
return Coordinator()
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<FocusableUIView>) {
guard uiView.window != nil, isFirstResponder, !context.coordinator.didBecomeFirstResponder else {
return
}
var foundRepsonder: UIView?
var currentSuperview: UIView? = uiView
repeat {
foundRepsonder = currentSuperview?.subviewFirstPossibleResponder
currentSuperview = currentSuperview?.superview
} while foundRepsonder == nil && currentSuperview != nil
guard let responder = foundRepsonder else {
return
}
DispatchQueue.main.async {
responder.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
}
}
}
private extension UIView {
var subviewFirstPossibleResponder: UIView? {
guard !canBecomeFirstResponder else { return self }
for subview in subviews {
if let firstResponder = subview.subviewFirstPossibleResponder {
return firstResponder
}
}
return nil
}
}
Voici un exemple d'utilisation de l'autofocus d'un champ de texte (+ bonus). @FocusState
nouvelle api iOS 15).
extension View {
@ViewBuilder
func autofocus() -> some View {
if #available(iOS 15, *) {
modifier(AutofocusedViewModifiers.Modern())
} else {
modifier(AutofocusedViewModifiers.Legacy())
}
}
}
private enum AutofocusedViewModifiers {
struct Legacy: ViewModifier {
func body(content: Content) -> some View {
content
.background(FocusableUIView(isFirstResponder: isFocused))
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isFocused = true
}
}
}
@State private var isFocused = false
}
@available(iOS 15, *)
struct Modern: ViewModifier {
func body(content: Content) -> some View {
content
.focused($isFocused)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isFocused = true
}
}
}
@FocusState private var isFocused: Bool
}
}
Exemple de vue du contenu :
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
TextField("placeholder", text: $text)
Text("some text")
}
.autofocus()
}
}
14 votes
Le fait que quelque chose de basique comme cela ne soit même pas supporté montre à quel point SwiftUI est immature pour le moment. Regardez les solutions ci-dessous, c'est ridicule pour quelque chose qui est censé rendre le codage de l'interface graphique "plus simple".