Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Suggest # tags while typing (like Twitter) for iPhone UITextView

I'd building an app that uses hashtags, like Twitter or Tweetbot. When you're typing a message, if you type the hashtag symbol, I'd like to suggest tags that match the current one you're typing.

I've already figured out how to get the UITableView to appear and show a list of hashtags, but what I can't figure out is how to do the following:

  1. Get the NSRange of the current word being typed,
  2. See if that range is formatted like a hashtag (NSRegularExpression @"#\\w\\w*")
  3. (From here on out, I've got the code figured out to search for matching hashtags and show them in the UITableView)

Can anyone help me with steps 1 and 2? I've been thinking about using textViewDidChange:, but I'm concerned that the app's performance might suffer if I'm constantly running methods every time the characters change.

Thanks!

like image 516
bryanjclark Avatar asked Nov 26 '11 19:11

bryanjclark


People also ask

Why does suggest mean?

to mention or introduce (an idea, proposition, plan, etc.) for consideration or possible action: The architect suggested that the building be restored. to propose (a person or thing) as suitable or possible for some purpose: We suggested him for president.

How do you use suggest correctly?

We can use suggest with the -ing form of a verb when we mention an action but do not mention the person who will do it, because it is understood in the context: She suggested travelling together for safety, since the area was so dangerous.

Does the word suggest mean recommend?

If you suggest something, you put forward a plan or idea for someone to think about. If you suggest the name of a person or place, you recommend them to someone. If you suggest that something is the case, you say something which you believe is the case.

What is the verb for suggest?

Regular verb: suggest - suggested - suggested.


1 Answers

I figured it out! I wound up using the textViewDidChange: and textViewDidChangeSelection: methods.

To get the NSRange of the current hashtag being typed, I ran a for loop over the NSRegularExpression matches in the text string. From there, I used NSLocationInRange to find out if the current cursor position intersected any of the hashtags.

Here's the code:

//Get the ranges of current hashtags
NSArray *hashtagRanges = [StringChecker rangesOfHashtagsInString:textView.text];
NSTextCheckingResult *currentHashtag;

if ([hashtagRanges count] >0)
{
    //List the ranges of all the hashtags
    for (int i = 0; i<[hashtagRanges count]; i++) 
    {
        NSTextCheckingResult *hashtag = [hashtagRanges objectAtIndex:i];
        //Check if the currentRange intersects the hashtag
        //Have to add an extra space to the range for if you're at the end of a hashtag. (since NSLocationInRange uses a < instead of <=)
        NSRange currentlyTypingHashtagRange = NSMakeRange(hashtag.range.location, hashtag.range.length + 1);
        if (NSLocationInRange(currentRange.location, currentlyTypingHashtagRange))
        {
            //If the cursor is over the hashtag, then snag that hashtag for matching purposes.
            currentHashtag = hashtag;
        }
    }

    if (currentHashtag){
        //If we found one hashtag that we're currently editing

        //Display the hashtag suggester, feed it the current hashtag for matching.
        [self showTagTable];

        //Get the current list of hashtags into an array
        NSFetchRequest *hashtagRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *tagEntityDescription = [NSEntityDescription entityForName:@"Tags" 
                                                                inManagedObjectContext:self.note.managedObjectContext];
        [hashtagRequest setEntity:tagEntityDescription];
        NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"dateLastUsed" 
                                                                         ascending:YES];
        NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
        [hashtagRequest setSortDescriptors:sortDescriptors];

        NSPredicate *tagPredicate = [NSPredicate predicateWithFormat:@"name contains[c] %@", [noteTextView.text substringWithRange:currentHashtag.range]];
        [hashtagRequest setPredicate:tagPredicate];

        tagsToDisplay = (NSMutableArray *)[self.note.managedObjectContext executeFetchRequest:hashtagRequest error:nil];
        [tagListTable reloadData];

        //If there are no matching hashtags, then let's hide the tag table.
        if ([tagsToDisplay count] == 0) 
        {
            [self hideTagTable];
            return;
        }

    }

The StringChecker class is a custom one that I wrote, it just has class methods that parse the strings. I made StringChecker a class because the methods are used in several places in the app. Here's the method:

    #pragma mark - Hashtag Methods
+(NSArray *)rangesOfHashtagsInString:(NSString *)string {
    NSRegularExpression *hashtagDetector = [[NSRegularExpression alloc] initWithPattern:@"#\\w\\w*" 
                                                                                options:NSRegularExpressionCaseInsensitive 
                                                                                  error:nil];    
    NSArray *hashtagRanges = [hashtagDetector matchesInString:string
                                                      options:NSMatchingWithoutAnchoringBounds
                                                        range:NSMakeRange(0, string.length)];
    return hashtagRanges;
}

+(NSUInteger)numberOfHashtagsInString:(NSString *)string {
    NSRegularExpression *hashtagDetector = [[NSRegularExpression alloc] initWithPattern:@"#\\w\\w*" 
                                                                                options:NSRegularExpressionCaseInsensitive 
                                                                                  error:nil];
    NSUInteger numberOfHashtags = [hashtagDetector numberOfMatchesInString:string
                                                                   options:NSRegularExpressionCaseInsensitive
                                                                     range:NSMakeRange(0, string.length)];
    return numberOfHashtags;
}
like image 60
bryanjclark Avatar answered Sep 28 '22 00:09

bryanjclark