Question

I have a user name system similar to twitters using the @ symbol.

I have it set up to message users by typing @username. This is basically identical to mentions in Twitter.

So far I have been able to detect the text following the @ symbol and to stop detecting when it hits white space. This has been done perfectly with NSScanner.

My problem is if I want to message multiple @usernames, the NSScanner will only detect the first occurrence. I need it to detect the latest occurrence. I have found this as a useful tool:

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];

I can't seem to figure out how to properly use this in conjunction with NSScanner. Here are 2 examples of my code attempts.

Example 1:

NSString *user = nil;
NSString *text = textView.text;

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];

if (lastSymbol.location != NSNotFound) {

    NSScanner *userScanner = [NSScanner scannerWithString:text];
    [userScanner scanUpToString:@"@" intoString:nil];

    NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@"@"];

    if (![userScanner isAtEnd]) {

        [userScanner scanUpToCharactersFromSet:charset intoString:nil];
        [userScanner scanCharactersFromSet:charset intoString:nil]
        [userScanner scanUpToCharactersFromSet:charset intoString:&user];


    }
}


if (user.length > 0) {

    NSRange whiteSpaceRange = [user rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
    if (whiteSpaceRange.location != NSNotFound) {

        // White space, username has ended

    } else {

        // Still getting username
        NSLog(@"%@", user);

    }

}

As you can see, I am just not quite sure how to make these work together to produce the results I am looking for. Any ideas? Thanks!

**************************UPDATE****************************

To be more specific, when a user types the @ symbol and starts typing a username, I want to display a list of their friends based on what they type. So naturally, I want to pull the string following the @ symbol and as the user keeps typing, keep updating the list of their friends. Once the user types a space, we assume they are done typing the user name (usernames cannot contain spaces) so I then stop displaying the table view.

The above code so far is simply detecting and pulling the string following the @ symbol. Here is the output right now:

Textfield Text: Hello @username

enter image description here

This works perfectly, as you can see I can keep checking this text against an array of friends.

Let's say the user wants to message another user in the same text like so:

Hello @username, this is my friend @goodfriend

This time it still displays the first @username, but ignore the second @goodfriend.

I figure the best way to do this is somehow tell the NSScanner to reference the LAST occurrence of the @ symbol, because what I have now is only referencing the first @ symbol.

This might also have to do with the way I am detecting the white space to know the user is done typing the username, and does not need any more suggestions.

Hope this helps, thanks!

Was it helpful?

Solution

For working with the live input like this, I would recommend using a completely different approach. Instead of looking for the last "@", look at just the last word, and see if it starts with "@".

NSString *user = nil;
NSString *text = textView.text;
// Find the last whitespace character
NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
NSRange lastWordRange = [text rangeOfCharacterFromSet:whitespace options:NSBackwardsSearch];
// If found, get the index after it. Otherwise, the entire string is one word.
if(lastWordRange.location != NSNotFound)
    lastWordRange.location += lastWordRange.length;
else lastWordRange.location = 0;
// If the index is not at the end, look for a '@' right after it
if(lastWordRange.location < [text length] &&
   [text characterAtIndex:lastWordRange.location] == '@') {
    // Found a '@', use the rest of the string as the username
    user = [text substringFromIndex:lastWordRange.location+1];
}
if(user) NSLog(@"%@", user); // The user is still typing a name

If you really want to use a scanner, you can use the same substringFromIndex: method to get a substring to scan.

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];
if (lastSymbol.location != NSNotFound) {
    NSString *substring = [text substringFromIndex:lastSymbol.location];
    NSScanner *userScanner = [NSScanner scannerWithString:substring];
    ...

OTHER TIPS

So here is the solution I came up with. @ugjoavgfhw has a method that also works with the limited testing I have done.

Here is my solution:

NSString *user = nil;
NSString *text = textView.text;

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];
NSScanner *userScanner = [NSScanner scannerWithString:text];
if (lastSymbol.location < 140) {
    [userScanner setScanLocation:lastSymbol.location];
}

[userScanner scanUpToString:@"@" intoString:nil];
NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@"@"];

if (![userScanner isAtEnd]) {        

    if (lastSymbol.location != NSNotFound) {

        [userScanner scanUpToCharactersFromSet:charset intoString:nil];
        [userScanner scanCharactersFromSet:charset intoString:nil];
        [userScanner scanUpToCharactersFromSet:charset intoString:&user];

    }

}


if (user.length > 0) {

    NSRange whiteSpaceRange = [user rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
    if (whiteSpaceRange.location != NSNotFound) {

        // White space, username has ended

    } else {

        // Still getting username
        NSLog(@"%@", user);

    }


}

Just to be clear, this code is called in the delegate method:

- (void)textViewDidChange:(UITextView *)textView

Every time the text updates, we perform a backward search looking for the @ symbol. This is assigned to an NSRange.

Then the NSScanner is created. Afterwards we check the lastSymbol.location to make sure it is somewhere below 140 characters (the format is that same as twitter with limited characters).

If it is, then we assign the scanning location to the NSScanner.

The rest works as expected. Hope this helps someone else!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top