RichEditorView icon indicating copy to clipboard operation
RichEditorView copied to clipboard

Focus issue ios 13

Open devgeektech opened this issue 5 years ago • 7 comments

Hello @cjwirth

Keyboard cursor appears only one time and suddenly disappear when I first tap on editor . But when I tap second time again on editor, cursor is showing. For the first time cursor disappear but keyboard exist. And User cannot type as cursor in not there.

devgeektech avatar Jan 06 '20 05:01 devgeektech

i have the same bug in ios 13 , and ifix it like this

RichEditorDelegate

func richEditorTookFocus(_ editor: RichEditorView) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5){ editor.focus() } }

mohammadabushalhoob avatar Jan 08 '20 11:01 mohammadabushalhoob

That solution refocuses the editor but ignores where the user tapped and always puts the cursor at the end. Is there a way to get it to refocus on the right position?

griffin-collins avatar Jan 08 '20 20:01 griffin-collins

I think I found a better solution. Create a subclass of RichEditorView and add another target to the gesture recognizer. Save the point when it gets a tap and use that when the delegate refocuses the editor

subclass:

import UIKit
import RichEditorView

class FBRichEditor: RichEditorView {
    public var lastFocus: CGPoint?
    public var tap: UITapGestureRecognizer!
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    
    public func configure() {
        tap = super.gestureRecognizers![0] as? UITapGestureRecognizer
        tap.addTarget(self, action: #selector(wasTapped))
        tap.delegate = self
        addGestureRecognizer(tap)
    }
    
    @objc private func wasTapped() {
        lastFocus = tap.location(in: super.webView)
    }
}

change richEditorTookFocus function to this:

func richEditorTookFocus(_ editor: RichEditorView) {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
            if self.richTextEditor.lastFocus != nil {
                editor.focus(at: self.richTextEditor.lastFocus!)
            } else {
                editor.focus()
            }
       }
}

griffin-collins avatar Jan 09 '20 15:01 griffin-collins

For my work this: @objc private func viewWasTapped() { let point = tapRecognizer.location(in: webView) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5){ self.focus(at: point) }

}

vabe1337 avatar Jan 13 '20 12:01 vabe1337

i have the same bug in ios 13 , and ifix it like this

RichEditorDelegate

func richEditorTookFocus(_ editor: RichEditorView) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5){ editor.focus() } }

Thanks this works for me.

pankajjakate avatar Feb 06 '20 07:02 pankajjakate

I think I found a better solution. Create a subclass of RichEditorView and add another target to the gesture recognizer. Save the point when it gets a tap and use that when the delegate refocuses the editor

subclass:

import UIKit
import RichEditorView

class FBRichEditor: RichEditorView {
    public var lastFocus: CGPoint?
    public var tap: UITapGestureRecognizer!
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    
    public func configure() {
        tap = super.gestureRecognizers![0] as? UITapGestureRecognizer
        tap.addTarget(self, action: #selector(wasTapped))
        tap.delegate = self
        addGestureRecognizer(tap)
    }
    
    @objc private func wasTapped() {
        lastFocus = tap.location(in: super.webView)
    }
}

change richEditorTookFocus function to this:

func richEditorTookFocus(_ editor: RichEditorView) {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
            if self.richTextEditor.lastFocus != nil {
                editor.focus(at: self.richTextEditor.lastFocus!)
            } else {
                editor.focus()
            }
       }
}

This seems like a good workaround! I ended up adding

func richEditorLostFocus(_ editor: RichEditorView) {
    self.richTextEditor.lastFocus = nil
}

so if you lose focus and then you programmatically need to give it focus again without a touch, the cursor appears at the end of the line as it should be

Still there has to be a reason why this is happening

I tried the referenced answer

additionalSafeAreaInsets.left = 12.0
additionalSafeAreaInsets.right = 12.0

but that did nothing

Hirobreak avatar Apr 08 '20 19:04 Hirobreak

Step 1 : Add focus

self.editorView.focus(at: .zero)

Step 2 : Add extension in RichEditorView typealias OldClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void typealias NewClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void

extension WKWebView{ 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)
    }
}

}

Finally set keyboardDisplayRequiresUserAction in private func setup()

webView.keyboardDisplayRequiresUserAction = false

IshuRocks avatar Dec 23 '22 08:12 IshuRocks