I am developing a custom keyboard for iOS.
As many will know, you have to embed the keyboard module into an iOS hosting app and after the app runs for the first time, you have to install the keyboard in iOS.
I did all this and the keyboard module appears correctly when invoked, but this is the problem.
I am using this code, in the keyboard module, to make it possible to write its view in SwiftUI.
import UIKit
import SwiftUI
class KeyboardViewController: UIInputViewController {
@IBOutlet var nextKeyboardButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let hostingController = UIHostingController(rootView: KeyboardView(viewController: self))
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(hostingController.view)
addChild(hostingController)
print("....", self.view.bounds)
self.nextKeyboardButton = UIButton(type: .system)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
print(self.view.bounds)
}
override func textWillChange(_ textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(_ textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
let textColor = self.textDocumentProxy.keyboardAppearance == UIKeyboardAppearance.dark ? UIColor.white : UIColor.black
self.nextKeyboardButton.setTitleColor(textColor, for: [])
}
}
Then I have this code to get the keyboard height.
struct KeyboardProvider: ViewModifier {
var keyboardHeight: Binding<CGFloat>
func body(content: Content) -> some View {
content
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification),
perform: { notification in
guard let userInfo = notification.userInfo,
let keyboardRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
self.keyboardHeight.wrappedValue = keyboardRect.height
}).onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification),
perform: { _ in
self.keyboardHeight.wrappedValue = 0
})
}
}
public extension View {
func keyboardHeight(_ state: Binding<CGFloat>) -> some View {
self.modifier(KeyboardProvider(keyboardHeight: state))
}
}
and I use it on the keyboard module as this:
@State private var keyboardHeight: CGFloat = 0
var body: some View {
Color.red
.keyboardHeight($keyboardHeight)
.onChange(of: keyboardHeight) {
print (keyboardHeight)
}
This prints the height as 852
that is the full height of the iPhone 15 I am testing it.
if I add the same instruction to the hosting app
var body: some View {
TextField("textField", text: $text)
.focused($focusedField, equals: .field)
.onAppear {
self.focusedField = .field
}
.padding()
.keyboardHeight($keyboardHeight)
.onChange(of: keyboardHeight) {
print (keyboardHeight)
}
}
the correct height is printed, and it is 370
In resume I have this situation:
- The hosting app loads and it knows the correct height the keyboard module will have.
- The keyboard loads and it thinks its height is the full screen.
How do I pass the correct height from the hosting app to the keyboard module?
I can think of a complex way using iCloud.
Is there a sane way to do that?