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

Focus / making the editor first responder #88

Open
gmckenzi opened this issue Feb 3, 2023 · 8 comments
Open

Focus / making the editor first responder #88

gmckenzi opened this issue Feb 3, 2023 · 8 comments

Comments

@gmckenzi
Copy link

gmckenzi commented Feb 3, 2023

I'm using this in a SwiftUI project and the only thing I haven't been able to figure out is how to programmatically set focus into the editor.

I have found StackOverflow posts that suggest WKWebView intentionally tries to prevent automatically setting focus without user interaction.

Is there a way in MarkupEditor that I can have it assume focus? My scenario is that I present the editor in a sheet and want the user to be able to start typing in the editor without having to tap into the editor first.

@gmckenzi
Copy link
Author

gmckenzi commented Feb 3, 2023

I see there is a bunch of swizzling code for focus in MarkupWKWebView that was commented out on Sep 5. If I restore that code and call showKeyboard() from makeUIView in the UI representable class, the editor gets focus and the keyboard appears.

I expect there was a good reason for commenting out this code and losing the functionality, but I'd love to know why or what the risks are of restoring this code.

@stevengharris
Copy link
Owner

stevengharris commented Feb 3, 2023

I did a lot of work on this issue that is still in main right now. Basically you can tell the MarkupEditor which view should be the firstResponder by its id:

MarkupEditor.firstResponder = <The String id of the MarkupWKWebView>

In the demos, the id is set to "Document" when the MarkupEditorView is created:

MarkupEditorView(markupDelegate: self, html: $demoHtml, resourcesUrl: resourcesUrl, id: "Document")

In the app I'm using MarkupEditor with, I have a unique id for the model-type object that holds onto the HTML I feed to the MarkupEditor.

Behind the scenes, the MarkupWKWebView observes the ObservedFirstResponder and responds:

/// Monitor the setting for MarkupEditor.observedFirstResponder, and set this MarkupWKWebView to be the first responder
/// when the id matches.
///
/// Becoming the first reponder also means that focus and selection state are set properly.
private func observeFirstResponder() {
    firstResponder = MarkupEditor.observedFirstResponder.$id.sink { [weak self] selectedId in
        guard let selectedId, let self, self.id == selectedId else {
            return
        }
        self.becomeFirstResponderIfReady()
    }
}

Main should be stable and I hope (but tbh not absolutely certain) compatible with the Beta release.

@gmckenzi
Copy link
Author

gmckenzi commented Feb 3, 2023

Awesome -- I'll point my package ref to main and give this a go. Thank you for such a prompt and detailed reply!

@stevengharris
Copy link
Owner

stevengharris commented Feb 4, 2023

Sorry, I think I sent you on a wild goose chase with main as opposed to the swizzling methods you found earlier. The work I did on firstResponder was useful and dealt with the case of it not working right because a view was not yet ready. In my usage, I have a List of MarkupWebViews, and I need to scroll to a particular one and make sure the insertion-point caret is showing properly, which is not the issue you're having. I even removed the swizzling methods for keyboard interaction from main because I figured they were not needed!!!

I think the reason I commented that code out was because, as you said, Apple mostly doesn't want you to be bringing up the keyboard without the user selecting something. Plus, in my own case, I want the view to open without the keyboard when on iOS, because I want it to be readable and scrollable without the keyboard covering the screen. I still think that will be the default case, but I can see adding a setting to make it optional, and then calling showKeyboard() at the right place when the option is set. That work would amount to adding a new static to MarkupEditor (so, for example, it could be set in your AppDelegate or someplace handy), and then gating the call to showKeyboard with a test on that setting. I'm not completely certain that makeUIView is the right place, but maybe. I might prefer it on a MarkupDelegate callback.

If you want to do that as a PR, I'd love to put it in. (FWIW, it would be easiest for me on main.) Otherwise, we can leave this open and I will try to deal with it at some point relatively soon.

I appreciate your raising the issue and can see how this can be very important. If you don't file a PR, please add any additional info about how you dealt with it if you think it will be useful to me and others.

Also, please note #89, which you will need to work around for now.

@gmckenzi
Copy link
Author

gmckenzi commented Mar 7, 2023

Thank you for all this info, and apologies for my delay in responding. I've been heads-down for the last little while.

Over the next week I'll focus on implementing this and reach out for feedback and/or submit a PR.

@anishjain123
Copy link

Has this been resolved? @gmckenzi @stevengharris

@stevengharris
Copy link
Owner

I haven't been able to spend any time on it. Have you tried what @gmckenzi suggested in #88 (comment)?

@JohnKuan
Copy link
Contributor

JohnKuan commented Aug 24, 2023

Hi @stevengharris, not sure if this is related.

In my use case, I have other UITextFields above the MarkupEditor. The MarkupEditor is the entire space below. When I try to tap on the space except the topmost line, it does not respond to being a first responder. However when I tap on the top most line, it becomes the first responder. This only happens for the first time, and after that it works properly.

RPReplay_Final1692860986.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants