twitter / TwitterTextEditor

A standalone, flexible API that provides a full-featured rich text editor for iOS applications.
Apache License 2.0
2.98k stars 164 forks source link

UITextViewDelegate-like functionality? #13

Open jshier opened 3 years ago

jshier commented 3 years ago

Question

UITextViewDelegate allows you to control whether the user's edits take effect through textView(_:shouldChangeTextIn:replacementText:). I can find no replacement for TwitterTextEditor. The closest I can find is TextEditorViewEditingContentDelegate's textEditorView(_:updateEditingContent) method, but returning nil from that method doesn't seem to have the affect I want (not applying the change). It seems like I should be able to create my own editing content, but there are no types which publicly conform to TextEditorViewEditingContent. Am I supposed to create my own conforming type? In any case, it seems like this functionality should be simpler.

jshier commented 3 years ago

Adding my own conforming type seems to work.

extension TextEditor: TextEditorViewEditingContentDelegate {
    func textEditorView(_ textEditorView: TextEditorView,
                        updateEditingContent editingContent: TextEditorViewEditingContent)
        -> TextEditorViewEditingContent? {
        guard editingContent.text.count > maximumCharacterCount else { return editingContent }

        let text = String(editingContent.text.prefix(maximumCharacterCount))

        return EditedContent(text: text,
                             selectedRange: NSRange(location: text.utf16.count, length: 0))
    }
}

private struct EditedContent: TextEditorViewEditingContent {
    var text: String

    var selectedRange: NSRange
}
niw commented 3 years ago

Hi, @jshier!

These APIs design is actually intentional.

It’s not documented by Apple, however, textView(_:shouldChangeTextIn:replacementText:) is very problematic API and we actually shouldn’t return false from this delegate method because it will brake many Input Method Program used for eastern Asian languages and some other languages. This fact is also described in the comment of Twitter Text Editor code (The marked text mentioned in this comment means the one typed by Input Method Program.) https://github.com/twitter/TwitterTextEditor/blob/master/Sources/TwitterTextEditor/TextEditorView.swift#L1660-L1672

Also replacementText is not always actual replacement text but sometimes faked control characters generated by the keyboard or separated symbols of a part of input characters, that means we can’t use it getting actual text changes.

Therefore, Twitter Text Editor’s textEditorView(_:updateEditingContent) is an alternative to it, which gives you the safe way to do what that problematic delegate provides to you.

And you’re true, we have to add own struct (or class) that conforms TextEditorViewEditingContent. This is mostly because that Twitter Text Editor also provides a simple way to wrap API for Objective-C usage.

By the way, for limiting number of characters, I recommend to not actually limit number of characters by the text editor level, but provides commit action to users such as adding Save or Send button to the interface then decide if application accepts the text or not when these buttons are tapped. Because users may want to type more and then polish it in regular typing situation, so if it is limited by the number characters, it might be annoying. Well, it’s just ”in general” for regular user experiences. It might be vary for the application requirements.

jshier commented 3 years ago

Thanks, good to know I'm on the right track. I certainly understand the difficulties with textView(_:shouldChangeTextIn:replacementText:), which is one of the reasons I'm using TwitterTextEditor (thanks!). You may want to vend a conforming type users can use to customize the response. As it is now, returning the incoming value or nil seem to do the same thing in the delegate method.

By the way, for limiting number of characters, I recommend to not actually limit number of characters by the text editor level, but provides commit action to users such as adding Save or Send button to the interface then decide if application accepts the text or not when these buttons are tapped. Because users may want to type more and then polish it in regular typing situation, so if it is limited by the number characters, it might be annoying. Well, it’s just ”in general” for regular user experiences. It might be vary for the application requirements.

That's what I argued for but the requirements went the other way. 😢