Search code examples
iosxcodeuitextviewunderline

iphone notes.app like underline


i have searched lot for the similar question and i decided to ask this through here, if already asked i could not find it and sorry.

i found that : UITextView like iPhone Notes application which redirects to here : http://www.cocoanetics.com/2010/03/stuff-you-learn-from-reverse-engineering-notes-app/ but in the reverse-engineering there is not the full code example !?

i am creating an application for iphone which stores and lets people enter their text like notes.app in iphone. i need to underline the text as i know and i need to do it in uitextview which is a subclass of uiscrollview ..

if anyone got any suggestion to make that i'll be happy..

thanks..


Solution

  • When I needed to do this with a UILabel, I created a subclass of UIView and implemented custom drawing myself. It's very detailed, but it's not that complex. I ended up passing in HTML-like syntax that handled for bold (< b > and < / b >) and new line (< br >) formatting. I think you could likely do the same for underline using (< u > and < / u >).

    Basically, my technique was to split the string using the HTML-like tokens (spitting first on the "br" tags, then on "b" tags) and then split each substring into "words", and then handle measuring and drawing each word on its own. If a word didn't fit on the current line, I'd wrap to the next line. It's not perfect, and it doesn't handle for a LOT of scenarios (for instance, a new line tag cannot be placed inside a bold tag), but it works for my intentions.

    Unfortunately, I don't know why text formatting isn't better supported in some (most/all?) of the iOS basic text display controls.


    Edit/Update:
    I'll go ahead and add the UIView subclass (replacement for UILabel) I wrote. Use at your own risk! :-)

    MMFormattedTextView.h

    #import <UIKit/UIKit.h><br>
    @interface MMFormattedTextView : UIView {
        int InsetLeft;
        int InsetTop;
        NSString *LabelText;
        UIFont *LabelFont;
    }
    @property (assign, nonatomic) int InsetLeft;
    @property (assign, nonatomic) int InsetTop;
    @property (strong, nonatomic) NSString *LabelText;
    @property (strong, nonatomic) UIFont *LabelFont;
    - (NSInteger)numberOfLinesForRect:(CGRect)rect;
    @end
    

    MMFormattedTextView.m

    #import "MMFormattedTextView.h"
    @implementation MMFormattedTextView
    @synthesize InsetLeft;
    @synthesize InsetTop;
    @synthesize LabelFont;
    @synthesize LabelText;
    // LIMITATION: Each bolded section must reside IN BETWEEN <br> tags; it MAY NOT span <br> tags!!!!
    - (void)drawRect:(CGRect)rect {
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextClearRect(ctx, rect);
        // Sets up the first position, which is 1 line "off the top", 
        // adjusted so that the text will be centered when it's all drawn
        CGFloat howManyLinesWouldFit = rect.size.height / [[self LabelFont] lineHeight];
        NSInteger howManyLinesDoWeHave = [self numberOfLinesForRect:rect];
        CGFloat lineOffset = (howManyLinesWouldFit - howManyLinesDoWeHave) / 2.0;
        CGPoint topLeft = CGPointMake([self InsetLeft], [self InsetTop] - [[self LabelFont] lineHeight] + (lineOffset * [[self LabelFont] lineHeight]));
        // Split the text into hard-split lines (actual <br> tags in the text)
        NSArray *lines = [[self LabelText] componentsSeparatedByString:@"<br>"];
        // Iterate through each hard-coded line
        for (NSString *line in lines) {
            // Iterate to the next line
            topLeft = CGPointMake([self InsetLeft], topLeft.y + [[self LabelFont] lineHeight]);
            NSArray *pieces = [line componentsSeparatedByString:@"<b>"];
            BOOL bold = YES;
            for (NSString *piece in pieces) {
                bold = !bold;
                UIFont *fontToUse;
                if (bold) {
                    fontToUse = [UIFont boldSystemFontOfSize:[[self LabelFont] pointSize]];
                } else {
                    fontToUse = [UIFont systemFontOfSize:[[self LabelFont] pointSize]];
                }
                NSArray *words = [piece componentsSeparatedByString:@" "];
                for (NSString *word in words) {
                    if ([word isEqualToString:@""]) continue;
                    NSString *wordWithSpace = [NSString stringWithFormat:@"%@ ", word];
                    CGSize wordSize = [wordWithSpace sizeWithFont:fontToUse];
                    if ((topLeft.x + wordSize.width) > (rect.size.width - [self InsetLeft])) {
                        // This runs off this line, so go to the next line
                        topLeft = CGPointMake([self InsetLeft], topLeft.y + [[self LabelFont] lineHeight]);
                    }
                    [wordWithSpace drawAtPoint:topLeft withFont:fontToUse];
                    topLeft = CGPointMake(topLeft.x + wordSize.width, topLeft.y);
                }
            }
        }
    }
    - (NSInteger)numberOfLinesForRect:(CGRect)rect {
        int retVal = 0;
        int left = [self InsetLeft];
        NSArray *lines = [[self LabelText] componentsSeparatedByString:@"<br>"];
        // Iterate through each hard-coded line
        for (NSString *line in lines) {
            // Iterate to the next line
            retVal = retVal + 1;
            left = [self InsetLeft];
            NSArray *pieces = [line componentsSeparatedByString:@"<b>"];
            BOOL bold = YES;
            for (NSString *piece in pieces) {
                bold = !bold;
                UIFont *fontToUse;
                if (bold) {
                    fontToUse = [UIFont boldSystemFontOfSize:[[self LabelFont] pointSize]];
                } else {
                    fontToUse = [UIFont systemFontOfSize:[[self LabelFont] pointSize]];
                }
                NSArray *words = [piece componentsSeparatedByString:@" "];
                for (NSString *word in words) {
                    if ([word isEqualToString:@""]) continue;
                    NSString *wordWithSpace = [NSString stringWithFormat:@"%@ ", word];
                    CGSize wordSize = [wordWithSpace sizeWithFont:fontToUse];
                    if ((left + wordSize.width) > (rect.size.width - [self InsetLeft])) {
                        // This runs off this line, so go to the next line
                        retVal = retVal + 1;
                        left = [self InsetLeft];
                    }
                    left = left + wordSize.width;
                }
            }
        }
        return retVal;
    }
    @end