RichEditorView icon indicating copy to clipboard operation
RichEditorView copied to clipboard

becomeFirstResponder or focus not working

Open Jcragons opened this issue 3 years ago • 6 comments

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 !

Jcragons avatar May 20 '21 15:05 Jcragons

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 avatar May 20 '21 23:05 Andrew-Chen-Wang

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 avatar May 21 '21 07:05 Jcragons

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 avatar May 21 '21 08:05 Jcragons

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.

Jcragons avatar May 21 '21 09:05 Jcragons

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.

Andrew-Chen-Wang avatar May 21 '21 14:05 Andrew-Chen-Wang

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 :)

Jcragons avatar May 21 '21 15:05 Jcragons