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:
- Get the
NSRange
of the current word being typed, - See if that range is formatted like a hashtag (
NSRegularExpression @"#\\w\\w*"
) - (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!
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;
}
Another way I figured out to do this is as follows.
In the - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
function I put a listener for a # being typed which begins recording the characters following the hash until the user types a space at which time it resets.
if ([text isEqualToString:@"#"]) {
recordingHashTag = YES;
startParse = range.location;
}else if ([text isEqualToString:@" "]) {
currentHashTag = nil;
recordingHashTag = NO;
theTable.hidden = YES;
}
if (recordingHashTag == YES) {
NSString *value;
if (startParse > [textView.text length] - startParse) {
value = [textView.text substringWithRange:NSMakeRange(startParse, [textView.text length] - startParse)];
[self filterHashTagTableWithHash:value];
}
}
If the BOOL recordingHashTag
is set to YES
I pass the substring
containing the hashtag text to a function which searches a pre populated array of hashtags. If there is a match it adds that entry to a filtered array of hashtags which it uses to populate the tableview
on the fly.
-(void)filterHashTagTableWithHash:(NSString *)hash{
[self.filterHashTagArray removeAllObjects];
for (NSString *hashTag in self.hashTagArray ){
NSRange result = [hashTag rangeOfString:hash options:NSCaseInsensitiveSearch];
if (result.location != NSNotFound) {
[filterHashTagArray addObject:hashTag];
}
}
if (filterHashTagArray.count) {
theTable.hidden = NO;
}else{
theTable.hidden = YES;
}
[self.theTable reloadData];
}
The final step is to insert the hash tag when the user clicks on the entry in the table.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = (UITableViewCell*)[self tableView:theTable cellForRowAtIndexPath:indexPath];
NSString *newString = [textViewComment.text stringByReplacingCharactersInRange:NSMakeRange(startParse, [textViewComment.text length] - startParse) withString:cell.textLabel.text];
textViewComment.text = newString;
}
Just don't forget to clear out your variables when a user backspaces mid hash tag.
来源:https://stackoverflow.com/questions/8281098/suggest-tags-while-typing-like-twitter-for-iphone-uitextview