Open mixtly87 opened 1 year ago
Yes I believe it is possible. In Resources/editor/rich_editor.js, you can create a new RE.fn
function that your Swift code in RichEditorView can call via runJs()
(runJs can also be called elsewhere, so long as you have access to the class). The internals of the Javascript function can be modeled after this stackoverflow answer: https://stackoverflow.com/a/30938898
Assuming we already know the position of the text, you can 1) focus in on the window (this might be run via another Swift command that calls runJs("focus")
that 2) next selects the range as minimally as possible that the user can type on.
In other words, if you can figure out how to do it via JavaScript like on any normal HTML website, you can do it here as well. Hope that helps
Thanks @Andrew-Chen-Wang for pointing me to the right direction. I was able to focus the web view and set the cursor at the beginning of the document by adding the code in viewDidAppear of relevant view controller:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
textView.webView.becomeFirstResponder()
textView.setCursorAtBegining()
}
and adding to RichEditorView.swift
:
public func setCursorAtBegining() {
runJS("RE.setCursorAtBegining()")
}
and also adding to rich_editor.js
:
RE.setCursorAtBegining = function() {
var el = document.getElementById("editor");
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[0], 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.focus();
}
however the problem I'm facing is that whole tableView is scrolled to bottom. So the RichEditorView
is embedded into a UITableViewCell (it's Mail Compose view controller), and the cell has a proper height to fit entire RichTextView with its entire content.
But this problem with scrolling... do you have a suggestion on how to prevent the UITableView from scrolling to bottom when RichTextView is focused?
I'm not sure which thing is scrolling to the bottom, but regardless, there is a JS event listener for focus that allows you to set clientX back to the top. If the table view is scrolling to the bottom, you can add a callback to Swift. I believe there is a callback that you can create using RE.callback that'll let you send back any data type. Then in Swift, you can adjust the scroll.
Btw focusing for me worked on simulator but not on device (iPhone Mini 12, iOS 16.3.1). What fixed it was this thread.
In viewDidAppear:
textView.webView.setKeyboardRequiresUserInteraction(false)
textView.focus(at: .zero)
and WKWebView extension:
extension WKWebView {
func setKeyboardRequiresUserInteraction( _ value: Bool) {
guard let WKContentViewClass = NSClassFromString("WKContentView") else { return }
typealias OlderClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
typealias NewerClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void
let olderSelector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
let iOS12_2Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
let iOS13Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:")
if let method = class_getInstanceMethod(WKContentViewClass, iOS13Selector) {
let originalImp: IMP = method_getImplementation(method)
let original: NewerClosureType = unsafeBitCast(originalImp, to: NewerClosureType.self)
let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
original(me, iOS13Selector, arg0, !value, arg2, arg3, arg4)
}
let imp: IMP = imp_implementationWithBlock(block)
method_setImplementation(method, imp)
} else if let method = class_getInstanceMethod(WKContentViewClass, iOS12_2Selector) {
let originalImp: IMP = method_getImplementation(method)
let original: NewerClosureType = unsafeBitCast(originalImp, to: NewerClosureType.self)
let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in
original(me, iOS12_2Selector, arg0, !value, arg2, arg3, arg4)
}
let imp: IMP = imp_implementationWithBlock(block)
method_setImplementation(method, imp)
} else if let method = class_getInstanceMethod(WKContentViewClass, olderSelector) {
let originalImp: IMP = method_getImplementation(method)
let original: OlderClosureType = unsafeBitCast(originalImp, to: OlderClosureType.self)
let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in
original(me, olderSelector, arg0, !value, arg2, arg3)
}
let imp: IMP = imp_implementationWithBlock(block)
method_setImplementation(method, imp)
}
}
}
I want to support email-compose kind of view so that when you forward or reply on existing email, it shows previous content bellow, setting the cursor at the top of the RichEditorView.
However, I'm not sure how to achieve that. Is it possible to set a caret cursor at specified location in RichEditorView?