I want to add line numbers to my UITextView
.
Do I have to write my own UI-Element, or is there an other solution?
There's nothing built-in for this. You'll have to do it yourself.
I accomplished this by subclassing UIView
and overriding the drawRect:
method like so:
#define TXT_VIEW_INSETS 8.0 // The default insets for a UITextView is 8.0 on all sides
@implementation NumberedTextView
@synthesize lineNumbers;
@synthesize delegate;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self setContentMode:UIViewContentModeRedraw];
internalScrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
[internalScrollView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[internalScrollView setBackgroundColor:[UIColor clearColor]];
[internalScrollView setClipsToBounds:YES];
[internalScrollView setScrollsToTop:YES];
[internalScrollView setContentSize:self.bounds.size];
[internalScrollView setContentMode:UIViewContentModeLeft];
[internalScrollView setDelegate:self];
[internalScrollView setBounces:NO];
internalTextView = [[UITextView alloc] initWithFrame:self.bounds];
[internalTextView setAutocapitalizationType:UITextAutocapitalizationTypeNone];
[internalTextView setAutocorrectionType:UITextAutocorrectionTypeNo];
[internalTextView setSpellCheckingType:UITextSpellCheckingTypeNo];
[internalTextView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[internalTextView setBackgroundColor:[UIColor clearColor]];
[internalTextView setClipsToBounds:YES];
[internalTextView setScrollsToTop:NO];
[internalTextView setContentMode:UIViewContentModeLeft];
[internalTextView setDelegate:self];
[internalTextView setBounces:NO];
[internalScrollView addSubview:internalTextView];
[self addSubview:internalScrollView];
}
return self;
}
- (void)drawRect:(CGRect)rect {
if (self.lineNumbers) {
[[internalTextView textColor] set];
CGFloat xOrigin, yOrigin, width/*, height*/;
uint numberOfLines = (internalTextView.contentSize.height + internalScrollView.contentSize.height) / internalTextView.font.lineHeight;
for (uint x = 0; x < numberOfLines; ++x) {
NSString *lineNum = [NSString stringWithFormat:@"%d:", x];
xOrigin = CGRectGetMinX(self.bounds);
yOrigin = ((internalTextView.font.pointSize + abs(internalTextView.font.descender)) * x) + TXT_VIEW_INSETS - internalScrollView.contentOffset.y;
width = [lineNum sizeWithFont:internalTextView.font].width;
// height = internalTextView.font.lineHeight;
[lineNum drawAtPoint:CGPointMake(xOrigin, yOrigin) withFont:internalTextView.font];
}
CGRect tvFrame = [internalTextView frame];
tvFrame.size.width = CGRectGetWidth(internalScrollView.bounds) - width;
tvFrame.size.height = MAX(internalTextView.contentSize.height, CGRectGetHeight(internalScrollView.bounds));
tvFrame.origin.x = width;
[internalTextView setFrame:tvFrame];
tvFrame.size.height -= TXT_VIEW_INSETS; // This fixed a weird content size problem that I've forgotten the specifics of.
[internalScrollView setContentSize:tvFrame.size];
}
}
#pragma mark - UITextView Delegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
[self setNeedsDisplay];
return YES;
}
- (void)textViewDidChange:(UITextView *)textView {
[self setNeedsDisplay];
}
- (void)textViewDidChangeSelection:(UITextView *)textView {
[self setNeedsDisplay];
}
#pragma mark - UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self setNeedsDisplay];
}