Search code examples
iosobjective-cuitextviewnsattributedstring

UITextView with @tags and hidden information behind the visuals


I'm trying to achieve same functionality as Facebook has with their post editor, where you can @tag people. I was able to easily achieve coloring using NSMutableAttributedString and NSRegularExpression.

After user posts a text I need to send some more information. User can select the person from autosuggestion popup (already implemented, getting the name and data for user). Everything is working as long as I want to just keep @people format, but I can't. The problem I have is is that I want to display @someone to user, but I need to keep more information in background, like userId for this person to pass it to backend later.

Check how Facebook does that - they don't even use @ sign, they just add some background color for name.

For example:

Here I tag @myfriend and something else

That should result in information that @tag start from 10 and has 9 length and userId=100, so I'm able to color it and pass to backend information about who exactly user picked. Also some additional functionality I want to have is to delete whole tag when user delete one letter from it.

I'm a bit struggling with this what would be the best way to do it.

Idea 1: Keeping 2 NSStrings, one having links in markupformat containing all data that lives in background and second one decorated to show to user.

Problems: Managing 2 NSString while user is editing is really painful, all NSRanges will be different for them and for example deleting tag from text will be tough. I tried to keep both synchronized but got so many problems with that and gave up.

Idea 2: Keeping an NSArray of references to tags when user is picking people to @tag with offset, length and showing user generated NSFormattedString which comes from combining a source text without any special markups and this information.

Problems: When user edits a text, ranges can change, for example if user deletes or adds some letter to my example before @myfriend, I will have to update offset for all tags after this, which can lead to some problems. More complicated will be if user selects a range of text and deletes it, than offset will have to be updated by more than 1.

Both sounds overcomplicated, problem is not easy I know, but maybe someone has some experience with such functionality or have some ideas how to solve that in some reasonable way.


Solution

  • The answer for my problem was idea number 2.

    I keep a text in UITextField + separately an array of all tags with their location and length - which is coming out of autocompletion views.

    During

    - (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
    

    I update all tags that are after the range that will be changed by incrementing their location by [text length] - range.length

    Also I have few more improvements that when user deletes a single letter from tag it whole dissapears. All done in this function works quite smoothly.

    Based on those generated tag locations I decorate a text.