0

I have an NSArray of NSStrings which I want to use as tokens in a UITextField, such that I cannot select or delete individual characters in the tokens. Overall, I want to create an effect like those of calculators where pressing backspace would delete a whole of a function, not just individual characters from it. The token also has to be highlightable together with the other text.

How should I go about doing this?

(I'm trying to achieve the behaviour of the text field as seen in Calc 2M)

Edit:

I figured out a solution to this by making my text field use attributed strings, and I set a special colour for my tokens. I then intercept text selections using a gesture recogniser to fire an event to move the start and end points of the selection to wrap around the token(s) instead of the actual selection. I don't think this is the best way to do this so I'll leave this as an edit rather than an answer.

Edit 2:

Another question that aligns with mine: Problems with <UITextinputDelegate> protocol implementation

Community
  • 1
  • 1
Jian Jie
  • 289
  • 1
  • 2
  • 13

2 Answers2

0

Under OS X this is done using a NSTokenField. There isn't an official implementation on iOS, but a few people rolled their own. Check this question for some examples: Is there an iPhone equivalent to the NSTokenField control?

Community
  • 1
  • 1
Alfonso
  • 8,386
  • 1
  • 43
  • 63
  • It seems that these solutions make it such that the token itself cannot be selected together with the text, which I forgot to mention I require. – Jian Jie Sep 09 '13 at 12:23
0

Alright, after trying out a ton of methods to get around this, I decided to just use UITextView in place of UITextField as UITextFieldDelegate has the following method:

- (void)textViewDidChangeSelection:(UITextView *)textView

Since I wanted my tokens to stand out in my UITextView, I decided to use NSAttributedString to highlight my text. Since the tokens are now has attributes different form the normal text, I can use the following to check if the selected text is a token:

- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange

I wrote a method that will take in any UITextPosition for a given UITextView and return the nearest left start of a token in the form of a NSInteger. This is so that I can easily use it to make a NSRange afterwards:

- (NSInteger)textViewWrappedIntegerFromPosition:(UITextPosition *)position forTextField:(UITextView *)textView
{
    NSInteger integer = [textView offsetFromPosition:textView.beginningOfDocument toPosition:position];
    NSInteger newInteger = integer;
    UIColor *color = [textView.attributedText attribute:NSForegroundColorAttributeName atIndex:integer-1 effectiveRange:NULL];

    // left of it is a character from a token
    if ([[UIColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0] isEqual:color])
    {
        NSInteger newInteger = integer+1;
        UITextPosition *startMinus = [textView positionFromPosition:position offset:-1];
        NSInteger startMinusInt = [textView offsetFromPosition:textView.beginningOfDocument toPosition:startMinus];
        UIColor *colorMinus = [textView.attributedText attribute:NSForegroundColorAttributeName atIndex:startMinusInt effectiveRange:NULL];

        // left and right of it is a character from a token
        if ([[UIColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0] isEqual:colorMinus]) // both collision
        {
            while ([colorMinus isEqual:[UIColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0]] && startMinusInt > 0)
            {
                // I used offset -1 again because when I wrote this I was testing something else
                position = [textView positionFromPosition:position offset:-1];
                startMinusInt = [textView offsetFromPosition:textView.beginningOfDocument toPosition:position];
                colorMinus = [textView.attributedText attribute:NSForegroundColorAttributeName atIndex:startMinusInt effectiveRange:NULL];
            }
            if (startMinusInt != 0)
            {
                newInteger = startMinusInt+1;
            }
            else
            {
                newInteger = startMinusInt;
            }
            return newInteger;
        }
        return newInteger;
    }
    return newInteger;
}

Using this method, I could compute if the start and end UITextPosition of a selection is in or adjacent to a token, and I can proceed to create a new range and set it as the new selection:

[textView setSelectedRange:NSRangeMake(newStartInt,newEndInt-newStartInt];
Jian Jie
  • 289
  • 1
  • 2
  • 13