I can only provide part of what you want. Here's some code that will scroll to the insertion point:
- (void) insertString: (NSString *) insertingString intoTextView: (UITextView *) textView {
NSRange range = textView.selectedRange;
NSString * firstHalfString = [textView.text substringToIndex:range.location];
NSString * secondHalfString = [textView.text substringFromIndex: range.location];
NSLog(@"insertString: location=%d", range.location);
textView.text = [NSString stringWithFormat: @"%@%@%@",
firstHalfString,
insertingString,
secondHalfString];
NSValue *value = [[NSValue valueWithRange:range] retain];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(doScroll:) userInfo:value repeats:NO];
NSLog(@"insertString: timer=%@", timer);
}
- (void)doScroll:(NSTimer*)theTimer {
NSValue *value = [theTimer userInfo];
NSRange range = [value rangeValue];
NSLog(@"doScroll: location=%d", range.location);
[theTextView scrollRangeToVisible:range];
[value release];
[theTimer invalidate];
}
The timer is necessary because scrollRangeToVisible: doesn't do what we want if it's called within the same event loop as [textView setText:]. Also I guess you know that the above can't be called unless textView is editing; i.e. I think both the keyboard and the caret must be visible else textView.selectedRange throws an exception.
I realize this isn't all of what you want. When we change the text property, the caret wants to go to the end of the new text and the view then wants to scroll until the caret is visible. I don't see any way to move the caret programmatically from outside the class.
So it looks like we'd need to subclass UITextView to be able to insert only the desired text. The caret would then travel to where we want and no scrolling would occur except to keep the caret visible.
Sure hope that helps. Best I can do for now.