From 975f2565cff7625f610c36cd7c146d8ab104a1dc Mon Sep 17 00:00:00 2001 From: Takuma Matsushita <46811718+takumatt@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:24:23 +0900 Subject: [PATCH] Add ignoresKeyboard option (#18) Co-authored-by: Muukii --- Development/Development/ContentView.swift | 79 ++++++++++++++++++- .../SwiftUIHosting/HostingController.swift | 28 +++++++ .../SwiftUIHosting/SwiftUIHostingView.swift | 6 ++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Development/Development/ContentView.swift b/Development/Development/ContentView.swift index 80d2504..6a76481 100644 --- a/Development/Development/ContentView.swift +++ b/Development/Development/ContentView.swift @@ -6,13 +6,23 @@ // import SwiftUI +import SwiftUIHosting struct ContentView: View { var body: some View { NavigationView { List { NavigationLink("Content") { - BookSizing() + BookSizing() + } + NavigationLink("Keyboard Avoidance ignores") { + KeyboardAvoidanceViewControllerRepresentable(ignoresKeyboard: true) + .edgesIgnoringSafeArea(.all) + } + + NavigationLink("Keyboard Avoidance not ignores") { + KeyboardAvoidanceViewControllerRepresentable(ignoresKeyboard: false) + .edgesIgnoringSafeArea(.all) } } } @@ -25,3 +35,70 @@ struct ContentView_Previews: PreviewProvider { } } +struct KeyboardAvoidanceViewControllerRepresentable: UIViewControllerRepresentable { + + let ignoresKeyboard: Bool + + init(ignoresKeyboard: Bool) { + self.ignoresKeyboard = ignoresKeyboard + } + + func makeUIViewController(context: Context) -> some UIViewController { + KeyboardAvoidanceViewController(ignoresKeyboard: ignoresKeyboard) + } + + func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { + + } + +} + +final class KeyboardAvoidanceViewController: UIViewController, UITextFieldDelegate { + + let ignoresKeyboard: Bool + + init(ignoresKeyboard: Bool) { + self.ignoresKeyboard = ignoresKeyboard + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError() + } + + override func viewDidLoad() { + super.viewDidLoad() + + let hostingView = SwiftUIHostingView(configuration: .init(ignoresKeyboard: ignoresKeyboard)) { + Text("Hello") + } + + view.addSubview(hostingView) + hostingView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + hostingView.rightAnchor.constraint(equalTo: view.rightAnchor), + hostingView.bottomAnchor + .constraint(equalTo: view.bottomAnchor, constant: -200), + hostingView.leftAnchor.constraint(equalTo: view.leftAnchor), + ]) + + let textField = UITextField() + textField.borderStyle = .roundedRect + textField.delegate = self + + textField.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(textField) + + NSLayoutConstraint.activate([ + textField.topAnchor.constraint(equalTo: view.topAnchor, constant: 100), + textField.centerXAnchor.constraint(equalTo: view.centerXAnchor), + ]) + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + +} diff --git a/Sources/SwiftUIHosting/HostingController.swift b/Sources/SwiftUIHosting/HostingController.swift index b3c26b2..28f6e19 100644 --- a/Sources/SwiftUIHosting/HostingController.swift +++ b/Sources/SwiftUIHosting/HostingController.swift @@ -10,6 +10,7 @@ final class HostingController: UIHostingController { init( accessibilityIdentifier: String? = nil, disableSafeArea: Bool, + ignoresKeyboard: Bool, rootView: Content ) { @@ -20,6 +21,33 @@ final class HostingController: UIHostingController { // waiting for iOS 16.4 as minimum deployment target _disableSafeArea = disableSafeArea + // https://steipete.com/posts/disabling-keyboard-avoidance-in-swiftui-uihostingcontroller/ + if ignoresKeyboard { + guard let viewClass = object_getClass(view) else { return } + + let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoresKeyboard") + if let viewSubclass = NSClassFromString(viewSubclassName) { + object_setClass(view, viewSubclass) + } + else { + guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } + guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return } + + if let method = class_getInstanceMethod(viewClass, NSSelectorFromString("keyboardWillShowWithNotification:")) { + let keyboardWillShow: @convention(block) (AnyObject, AnyObject) -> Void = { _, _ in + if ignoresKeyboard { + + } else { + + } + } + class_addMethod(viewSubclass, NSSelectorFromString("keyboardWillShowWithNotification:"), + imp_implementationWithBlock(keyboardWillShow), method_getTypeEncoding(method)) + } + objc_registerClassPair(viewSubclass) + object_setClass(view, viewSubclass) + } + } } @MainActor required dynamic init?(coder aDecoder: NSCoder) { diff --git a/Sources/SwiftUIHosting/SwiftUIHostingView.swift b/Sources/SwiftUIHosting/SwiftUIHostingView.swift index fedd982..20c07f8 100644 --- a/Sources/SwiftUIHosting/SwiftUIHostingView.swift +++ b/Sources/SwiftUIHosting/SwiftUIHostingView.swift @@ -25,6 +25,7 @@ open class SwiftUIHostingView: UIView { self.hostingController = HostingController( accessibilityIdentifier: _typeName(Content.self), disableSafeArea: configuration.disableSafeArea, + ignoresKeyboard: configuration.ignoresKeyboard, rootView: usingContent ) @@ -32,6 +33,7 @@ open class SwiftUIHostingView: UIView { self.hostingController = HostingController( disableSafeArea: configuration.disableSafeArea, + ignoresKeyboard: configuration.ignoresKeyboard, rootView: usingContent ) @@ -231,6 +233,8 @@ public struct SwiftUIHostingConfiguration { */ public var disableSafeArea: Bool + public var ignoresKeyboard: Bool + public var sizeMeasureMode: SwiftUIHostingSizeMeasureMode public var baseModifier: BaseModifier @@ -238,11 +242,13 @@ public struct SwiftUIHostingConfiguration { public init( registersAsChildViewController: Bool = true, disableSafeArea: Bool = true, + ignoresKeyboard: Bool = false, sizeMeasureMode: SwiftUIHostingSizeMeasureMode = .systemSizeThatFits, baseModifier: BaseModifier = .shared ) { self.registersAsChildViewController = registersAsChildViewController self.disableSafeArea = disableSafeArea + self.ignoresKeyboard = ignoresKeyboard self.sizeMeasureMode = sizeMeasureMode self.baseModifier = baseModifier }