CustomKeyboardKit
CustomKeyboardKit copied to clipboard
Wrong keyboard shown first time after focusing on a field shown conditionally
Hello, I hope you are doing well!
I'm not sure that this is an issue with this library, but I was curious to hear your thoughts.
Description
Background
SwiftUI supports conditionally showing views, by letting you place if-else
or switch
blocks directly in the view code. This allows SwiftUI to manage when an element should be displayed or removed from the view (other approaches involve modifying an element's opacity conditionally, but that element will still take up space when invisible).
Expectation
When using a conditional to switch to a TextField that has a custom keyboard set on it, I want to also focus the TextField to automatically present the keyboard. However, when doing so, the "standard" keyboard is presented instead. After toggling the conditional again, the TextField can present the custom keyboard.
I don't know how the introspect library works, but I'm guessing someone will tell me that conditional blocks in SwiftUI prevents a view from entering memory or something...
Demonstration
Code to replicate:
import SwiftUI
import CustomKeyboardKit
struct ContentView: View {
@State private var selectedEntryType = EntryType.date
@State private var textFieldContent = ""
@FocusState private var textFieldFocused
var body: some View {
Form {
Picker("Entry Type", selection: $selectedEntryType) {
Text(EntryType.date.rawValue)
.tag(EntryType.date)
Text(EntryType.binary.rawValue)
.tag(EntryType.binary)
}
.pickerStyle(.segmented)
.onChange(of: selectedEntryType) { oldState, newState in
if newState == .binary {
textFieldFocused = true
}
}
switch selectedEntryType {
case .date:
DatePicker("some date", selection: Binding(get: { Date.now }, set: {_ in}))
case .binary:
TextField("title", text: $textFieldContent, axis: .vertical)
.customKeyboard(.binary)
.focused($textFieldFocused)
}
}
}
}
private enum EntryType: String, CaseIterable {
case date = "Date"
case binary = "Binary"
}
extension CustomKeyboard {
static var binary: CustomKeyboard {
let keyWidth = 20.0
let keyHeight = 34.0
return CustomKeyboardBuilder { textDocumentProxy, submit, playSystemFeedback in
HStack {
Button {
textDocumentProxy.insertText("0")
playSystemFeedback?()
} label: {
Text("0")
.frame(minWidth: keyWidth, minHeight: keyHeight)
}
Button {
textDocumentProxy.insertText("1")
playSystemFeedback?()
} label: {
Text("1")
.frame(minWidth: keyWidth, minHeight: keyHeight)
}
.padding(.trailing, 10)
Button {
textDocumentProxy.deleteBackward()
playSystemFeedback?()
} label: {
Image(systemName: "delete.backward")
.frame(minHeight: keyHeight)
}
Button {
playSystemFeedback?()
submit()
} label: {
Text("Done")
.frame(minHeight: keyHeight)
}
}
.font(.title)
.buttonStyle(.bordered)
.padding([.top, .bottom], 20)
}
}
}
My workaround
This little hack is the only way I've found to resolve the issue so far.
// Updating the focus assignment in the demo code:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
textFieldFocused = true
}