Unwanted Vertical Padding from iOS 6 on CATextLayer

前端 未结 5 1026
半阙折子戏
半阙折子戏 2021-02-06 15:37

Background: I started my project in iOS 5 and built out a beautiful button with layer. I added a textLayer onto the button and center it using the following cod

5条回答
  •  说谎
    说谎 (楼主)
    2021-02-06 16:00

    Wile I am waiting for an ultimate solution, I studied about RTLabel and TTTAttributedLabel, and made a simple class to draw text on a CALayer as Steve suggested. Hope it helps, and please don't hesitant to point out any mistake I have made.

    CustomTextLayer.h

    #import 
    
    @interface CustomTextLayer : CALayer {
        NSString                        *_text;
        UIColor                         *_textColor;
    
        NSString                        *_font;
        float                           _fontSize;
    
        UIColor                         *_strokeColor;
        float                           _strokeWidth;
    
        CTTextAlignment                 _textAlignment;
        int                             _lineBreakMode;
    
        float                           _suggestHeight;
    }
    
    -(float) suggestedHeightForWidth:(float) width;
    
    @property (nonatomic, retain) NSString *text;
    @property (nonatomic, retain) UIColor *textColor;
    
    @property (nonatomic, retain) NSString *font;
    @property (nonatomic, assign) float fontSize;
    
    @property (nonatomic, retain) UIColor *strokeColor;
    @property (nonatomic, assign) float strokeWidth;
    
    @property (nonatomic, assign) CTTextAlignment textAlignment;
    
    @end
    

    CustomTextLayer.m

    #import 
    #import "CustomTextLayer.h"
    
    @implementation CustomTextLayer
    
    @synthesize text = _text, textColor = _textColor;
    @synthesize font = _font, fontSize = _fontSize;
    @synthesize strokeColor = _strokeColor, strokeWidth = _strokeWidth;
    @synthesize textAlignment = _textAlignment;
    
    -(id) init {
        if (self = [super init]) {
            _text = @"";
            _textColor = [UIColor blackColor];
    
            _font = @"Helvetica";
            _fontSize = 12;
    
            _strokeColor = [UIColor whiteColor];
            _strokeWidth = 0.0;
    
            _textAlignment = kCTLeftTextAlignment;
            _lineBreakMode = kCTLineBreakByWordWrapping;
    
        }
        return self;
    }
    
    -(void) dealloc {
        [_text release];
        [_textColor release];
    
        [_font release];
    
        [_strokeColor release];
    
        [super dealloc];
    }
    
    -(void) setText:(NSString *)text {
        [_text release];
        _text = [text retain];
        [self setNeedsDisplay];
    }
    
    -(void) setTextColor:(UIColor *)textColor {
        [_textColor release];
        _textColor = [textColor retain];
        [self setNeedsDisplay];
    }
    
    -(void) setFont:(NSString *)font {
        [_font release];
        _font = [font retain];
        [self setNeedsDisplay];
    }
    
    -(void) setFontSize:(float)fontSize {
        _fontSize = fontSize;
        [self setNeedsDisplay];
    }
    
    -(void) setStrokeColor:(UIColor *)strokeColor {
        [_strokeColor release];
        _strokeColor = strokeColor;
        [self setNeedsDisplay];
    }
    
    -(void) setStrokeWidth:(float)strokeWidth {
        _strokeWidth = 0 ? (strokeWidth < 0) : (-1 * strokeWidth);
        [self setNeedsDisplay];
    }
    
    -(void) setTextAlignment:(CTTextAlignment)textAlignment {
        _textAlignment = textAlignment;
        [self setNeedsDisplay];
    }
    
    -(void) setFrame:(CGRect)frame {
        [super setFrame: frame];
        [self setNeedsDisplay];
    }
    
    -(float) suggestedHeightForWidth:(float) width {
    
        CTFontRef fontRef = CTFontCreateWithName((CFStringRef)_font, _fontSize, NULL);
    
        CTParagraphStyleSetting paragraphStyles[2] = {
            {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *) &_lineBreakMode},
            {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void *) &_textAlignment}
        };
        CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 2);
    
        NSDictionary *attrDict = [[NSDictionary alloc] initWithObjectsAndKeys:(id)fontRef, (NSString *)kCTFontAttributeName, (id)_textColor.CGColor, (NSString *)(kCTForegroundColorAttributeName), (id)_strokeColor.CGColor, (NSString *)(kCTStrokeColorAttributeName), (id)[NSNumber numberWithFloat: _strokeWidth], (NSString *)(kCTStrokeWidthAttributeName), (id)paragraphStyle, (NSString *)(kCTParagraphStyleAttributeName), nil];
    
        CFRelease(fontRef);
        CFRelease(paragraphStyle);
    
        NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:_text attributes: attrDict];
    
        // Determine suggested frame height
        CFRange textRange = CFRangeMake(0, [attrStr length]);
        CGSize constraint = CGSizeMake(width, 9999);
    
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
        CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, textRange, NULL, constraint, NULL);
        textSize = CGSizeMake(ceilf(textSize.width), ceilf(textSize.height));
    
        [attrDict release];
        [attrStr release];
    
        return textSize.height;
    }
    
    -(void) renderText:(CGContextRef)ctx {
        CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
        CGContextTranslateCTM(ctx, 0, self.bounds.size.height);
        CGContextScaleCTM(ctx, 1.0, -1.0);
    
        CTFontRef fontRef = CTFontCreateWithName((CFStringRef)_font, _fontSize, NULL);
    
        CTParagraphStyleSetting paragraphStyles[2] = {
            {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *) &_lineBreakMode},
            {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void *) &_textAlignment}
        };
        CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 2);
    
        NSDictionary *attrDict = [[NSDictionary alloc] initWithObjectsAndKeys:(id)fontRef, (NSString *)kCTFontAttributeName, (id)_textColor.CGColor, (NSString *)(kCTForegroundColorAttributeName), (id)_strokeColor.CGColor, (NSString *)(kCTStrokeColorAttributeName), (id)[NSNumber numberWithFloat: _strokeWidth], (NSString *)(kCTStrokeWidthAttributeName), (id)paragraphStyle, (NSString *)(kCTParagraphStyleAttributeName), nil];
    
        CFRelease(fontRef);
        CFRelease(paragraphStyle);
    
        NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:_text attributes: attrDict];
    
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, self.bounds);
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
    
        CFRange textRange = CFRangeMake(0, [attrStr length]);
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL);
        CFArrayRef lines = CTFrameGetLines(frame);
        NSInteger numberOfLines = CFArrayGetCount(lines);
        CGPoint lineOrigins[numberOfLines];
        CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins);
    
        for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {
            CGPoint lineOrigin = lineOrigins[lineIndex];
            CGContextSetTextPosition(ctx, lineOrigin.x,  lineOrigin.y);
            CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
    
            if (lineIndex == numberOfLines - 1) {
                CFRange lastLineRange = CTLineGetStringRange(line);
    
                if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) {
                    NSUInteger truncationAttributePosition = lastLineRange.location;
                    CTLineTruncationType truncationType;
                    if (numberOfLines != 1) {
                        truncationType = kCTLineTruncationEnd;
                        truncationAttributePosition += (lastLineRange.length - 1);
                    }
    
                    NSAttributedString *tokenString = [[NSAttributedString alloc] initWithString:@"\u2026" attributes:attrDict];
                    CTLineRef truncationToken = CTLineCreateWithAttributedString((CFAttributedStringRef)tokenString);
    
                    NSMutableAttributedString *truncationString = [[attrStr attributedSubstringFromRange: NSMakeRange(lastLineRange.location, lastLineRange.length)] mutableCopy];
                    if (lastLineRange.length > 0) {
                        unichar lastCharacter = [[truncationString string] characterAtIndex: lastLineRange.length - 1];
                        if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) {
                            [truncationString deleteCharactersInRange:NSMakeRange(lastLineRange.length - 1, 1)];
                        }
                    }
                    [truncationString appendAttributedString: tokenString];
                    CTLineRef truncationLine = CTLineCreateWithAttributedString((CFAttributedStringRef) truncationString);
    
                    CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, self.bounds.size.width, truncationType, truncationToken);
                    if (!truncatedLine) {
                        // If the line is not as wide as the truncationToken, truncatedLine is NULL
                        truncatedLine = CFRetain(truncationToken);
                    }
    
                    CTLineDraw(truncatedLine, ctx);
    
                    CFRelease(truncatedLine);
                    CFRelease(truncationLine);
                    CFRelease(truncationToken);
                } else {
                    CTLineDraw(line, ctx);
                }
            } else {
                CTLineDraw(line, ctx);
            }
        }
    
        [attrStr release];
        [attrDict release];
    
        CFRelease(path);
        CFRelease(frame);
        CFRelease(framesetter);
    
    }
    
    -(void) drawInContext:(CGContextRef)ctx {
        [super drawInContext: ctx];
        [self renderText: ctx];
    }
    
    @end
    

提交回复
热议问题