Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

becomeFirstResponder or focus not working #19

Open
Jcragons opened this issue May 20, 2021 · 6 comments
Open

becomeFirstResponder or focus not working #19

Jcragons opened this issue May 20, 2021 · 6 comments
Labels
bug Something isn't working

Comments

@Jcragons
Copy link

Hi :) On simulator here on 14.5, i can't get working becomeFirstResponder or focus method. It's working fine with finger tap, showing the keyboard, but programmatically never.

I'm using it inside tableview cell, and try to add a new cell, then show the focus directly, and it's never fired. Look like the method in js is called (with breakpoints) but never get the focus in the simulator.

Any idea or same reaction ? thx !

@Andrew-Chen-Wang
Copy link
Owner

When you do becomeFirstResponder, have you tried just typing on your keyboard? Does the toolbar appear? Additionally, if you have an iPhone do you mind trying it out on there programmatically and seeing if the keyboard appears? There is also a method called focus. Do you mind trying that out as well?

Please let me know what happens for each of those requests; really helps me debug :) Cheers!

@Andrew-Chen-Wang Andrew-Chen-Wang added the bug Something isn't working label May 20, 2021
@Jcragons
Copy link
Author

No keyboard appeared, i'm on iPad devices, the editor view with the decoded html appeard well, but no focus and so no keyboard. I'm using this object only on iPad. I've tried becomeFirstResponder and focus, even focusAt, nohting but the callback richEditorTookFocus is called everytime, so maybe something related to the web part.

Could a css could prevent the focus programmatically ? Because it works well when i tap. I don't think there is a way to simulate a tap without UItouch event.

@Jcragons
Copy link
Author

Or, which could be a bad news, could be related to that : the removal of keyboardDisplayRequiresUserAction not available in WKWebView compare to UIWebView ?

https://developer.apple.com/documentation/uikit/uiwebview/1617967-keyboarddisplayrequiresuseractio#discussion

Could be a solution ?
https://stackoverflow.com/questions/32449870/programmatically-focus-on-a-form-in-a-webview-wkwebview

Because i've event put a RE.focus() at the end of document ready on rich_editor.js and it's called after (breakpoint and js debug fired) but no focus or keyboard shown :(

@Jcragons
Copy link
Author

Jcragons commented May 21, 2021

ok, it was that ... thx Apple for always breaking everything ;) anyway here some code i've used

import WebKit
import Foundation

typealias OldClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
typealias NewClosureType =  @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void

public class RichEditorWebView: WKWebView {
    public var accessoryView: UIView?
    
    public override var inputAccessoryView: UIView? {
        return accessoryView
    }
    
    private var _keyboardDisplayRequiresUseraction = true

    public var keyboardDisplayRequiresUserAction: Bool? {
            get {
                return self.keyboardDisplayRequiresUserAction
            }
            set {
                self.setKeyboardRequiresUserInteraction(newValue ?? true)
            }
        }

        func setKeyboardRequiresUserInteraction( _ value: Bool) {
            guard let WKContentView: AnyClass = NSClassFromString("WKContentView") else {
                print("keyboardDisplayRequiresUserAction extension: Cannot find the WKContentView class")
                return
            }
            // For iOS 10, *
            let sel_10: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
            // For iOS 11.3, *
            let sel_11_3: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
            // For iOS 12.2, *
            let sel_12_2: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
            // For iOS 13.0, *
            let sel_13_0: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:")

            if let method = class_getInstanceMethod(WKContentView, sel_10) {
                let originalImp: IMP = method_getImplementation(method)
                let original: OldClosureType = unsafeBitCast(originalImp, to: OldClosureType.self)
                let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
                    original(me, sel_10, arg0, !value, arg2, arg3)
                }
                let imp: IMP = imp_implementationWithBlock(block)
                method_setImplementation(method, imp)
            }

            if let method = class_getInstanceMethod(WKContentView, sel_11_3) {
                let originalImp: IMP = method_getImplementation(method)
                let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
                let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                    original(me, sel_11_3, arg0, !value, arg2, arg3, arg4)
                }
                let imp: IMP = imp_implementationWithBlock(block)
                method_setImplementation(method, imp)
            }

            if let method = class_getInstanceMethod(WKContentView, sel_12_2) {
                let originalImp: IMP = method_getImplementation(method)
                let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
                let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                    original(me, sel_12_2, arg0, !value, arg2, arg3, arg4)
                }
                let imp: IMP = imp_implementationWithBlock(block)
                method_setImplementation(method, imp)
            }

            if let method = class_getInstanceMethod(WKContentView, sel_13_0) {
                let originalImp: IMP = method_getImplementation(method)
                let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self)
                let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
                    original(me, sel_13_0, arg0, !value, arg2, arg3, arg4)
                }
                let imp: IMP = imp_implementationWithBlock(block)
                method_setImplementation(method, imp)
            }
        }
}

and then in setup part

webView.keyboardDisplayRequiresUserAction = false

look like it's working here on iPad ios 14.5 :) i don't like private method, but look like there is no other options here, or i didn't look enough on WKWebView docs.

@Andrew-Chen-Wang
Copy link
Owner

Andrew-Chen-Wang commented May 21, 2021

Thanks for this investigation @Jcragons ! Did you have this issue on previous iPadOS or iOS versions? And if you'd like, a PR is also appreciated :)

Scratch that: I see:

// For iOS 10, *
            let sel_10: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
            // For iOS 11.3, *
            let sel_11_3: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
            // For iOS 12.2, *
            let sel_12_2: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
            // For iOS 13.0, *
            let sel_13_0: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:")

which is... interesting to say the least.

@Jcragons
Copy link
Author

Yeah i assume this removal from apple render people crazy since everyone need to move from UIWebView to WKWebView. Didn't try under < ios12 or right now anything else than ipadOs, but i assume it's global. I hope the private method hack will last, look like on ios14 they didn't change method signature, but i'll cross finder at every release ;)

I'm not directly using the files from github, but i can check for a PR as soon as i finish my inegration :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants