【问题标题】:Is there a way to control line height for multiline text drawn by Quartz in iPhone?有没有办法控制 iPhone 中 Quartz 绘制的多行文本的行高?
【发布时间】:2011-11-06 13:21:00
【问题描述】:

我在表格视图中使用 Quartz 进行单元格渲染。它工作得很好,但为了满足设计理念,我必须以某种方式找到调整多行文本行高的方法。 目前我正在使用方便的 UIKit 添加到 NSString 来呈现文本:

– drawInRect:withFont:lineBreakMode:alignment:

但是,我在任何地方都找不到设置行距的文档。 UIWebView 能够做到这一点,因此它必须使用一些较低级别的 API 来计算行高 CSS 属性。
你能推荐兼容iOS 3.0、3.1的解决方案吗? 我知道我可以尝试使用 Core Text,但它可以从 iOS 3.2 获得。

【问题讨论】:

  • 您真的要保持与 iOS 3 的兼容性吗?唯一不升级的人可能从未使用过应用商店(或将他们的设备连接到他们的台式机)。
  • 这很有趣,我想到了这一点。但难不成也有 iPhone 3G 的人因为性能原因不更新到 iOS4 吗?

标签: iphone objective-c cocoa-touch quartz-graphics multiline


【解决方案1】:

我认为这里的简短回答是否定的。我已经解决了这个问题一段时间,最终不得不为标签实施我自己的解决方案。我将在下面发布此解决方案。希望它有助于你自己实现一些东西。请原谅文件的长度和数量,为了可测试性,我将其分解,它也可以处理省略号。

//MHLabel.h
#import <UIKit/UIKit.h>
#import "MHTextRuler.h"

@interface MHLabel : UIView<MHTextRuler> {
}

@property (nonatomic, copy) NSString *text;
@property (nonatomic, retain) UIFont *font;
@property (nonatomic, retain) UIColor *textColor;
@property (nonatomic) UITextAlignment textAlignment;
@property (nonatomic) CGFloat lineSpacingMuliplier;
@property (nonatomic) int maxLines;
@property (nonatomic, readonly) int currentLines;
@property (nonatomic, readonly) BOOL ellipsized;

- (CGFloat) constrainHeightForCurrentWidth;

@end


//MHLabel.m
#import "MHLabel.h"
#import "UILabel+MH.h"
#import "MHUI.h"
#import "MHLabelLines.h"
#import "MHLabelLayout.h"

@interface MHLabel() 

- (MHLabelLines *) linesForWidth: (CGFloat) width;

@property (nonatomic, retain) NSMutableDictionary *lineCache;

@end

@implementation MHLabel

@synthesize text = _text;
@synthesize font = _font;
@synthesize textColor = _textColor;
@synthesize textAlignment = _textAlignment;
@synthesize lineSpacingMuliplier = _lineSpacingMuliplier;
@synthesize maxLines = _maxLines;
@synthesize lineCache = _lineCache;

- (id) initWithFrame: (CGRect) frame {
    if ((self = [super initWithFrame: frame])) {
        self.opaque = NO;
        self.maxLines = 0;
        self.lineSpacingMuliplier = 1;
        self.textAlignment = UITextAlignmentLeft;
        self.font = [MHUI fontOfSize: 16];
        self.textColor = [MHUI darkGrayText];
        self.lineCache = [NSMutableDictionary dictionary];
        self.contentMode = UIViewContentModeTopLeft;
    }
    return self;
}

- (void) dealloc {
    self.text = nil;
    self.font = nil;
    self.textColor = nil;
    self.lineCache = nil;
    [super dealloc];
}

- (void) setFrame: (CGRect) frame {
    [super setFrame: frame];
    [self setNeedsDisplay];
}

- (void) setText: (NSString *) text {
    if (![text isEqualToString: _text]) {
        [_text release];
        _text = [text copy];
        [self.lineCache removeAllObjects];
        [self setNeedsDisplay];
    }
}

- (void) setFont: (UIFont *) font {
    if (![font isEqual: _font]) {
        [_font release];
        _font = font;
        [_font retain];
        [self.lineCache removeAllObjects];
        [self setNeedsDisplay];
    }
}

- (void) setMaxLines: (NSInteger) maxLines {
    if (maxLines != _maxLines) {
        _maxLines = maxLines;
        [self.lineCache removeAllObjects];
        [self setNeedsDisplay];
    }
}

- (void) setTextColor: (UIColor *) textColor {
    if (![textColor isEqual: _textColor]) {
        [_textColor release];
        _textColor = textColor;
        [_textColor retain];
        [self setNeedsDisplay];
    }
}

- (void) setLineSpacingMuliplier: (CGFloat) lineSpacingMuliplier {
    if (lineSpacingMuliplier != _lineSpacingMuliplier) {
        _lineSpacingMuliplier = lineSpacingMuliplier;
        [self setNeedsDisplay];
    }
}

- (MHLabelLines *) linesForWidth: (CGFloat) width {
    NSString *key = [NSString stringWithFormat: @"w%d", (int) width];
    if (![self.lineCache objectForKey: key]) {
        MHLabelLines *labelLines = [MHLabelLayout linesForText: self.text constrainedToLines: self.maxLines andWidth: width withRuler: self];
        [self.lineCache setObject: labelLines forKey: key];
    }
    return [self.lineCache objectForKey: key];
}

- (CGFloat) constrainHeightForCurrentWidth {
    CGSize size = [self sizeThatFits: CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)];
    CGRect newFrame = self.frame;
    newFrame.size = size;
    self.frame = newFrame;
    return size.height;
}

- (CGSize) sizeThatFits: (CGSize) size {
    CGFloat width = size.width;
    NSArray *lines = [self linesForWidth: width].lines;
    CGFloat textHeight = [@"X" sizeWithFont: self.font].height;
    CGFloat height = floorf(textHeight * self.lineSpacingMuliplier) * [lines count];
    return CGSizeMake(width, height);
}

- (void) drawRect: (CGRect) rect {
    CGFloat width = rect.size.width;
    NSArray *lines = [self linesForWidth: width].lines;
    CGFloat y = 0;
    CGFloat textHeight = [@"X" sizeWithFont: self.font].height;
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
    for (NSString *string in lines) {
        CGFloat x;
        if (self.textAlignment == UITextAlignmentLeft) {
            x = 0;
        } else {
            CGFloat textWidth = [string sizeWithFont: self.font].width;
            if (self.textAlignment == UITextAlignmentCenter) {
                x = MAX(0, roundf((width - textWidth) / 2));
            } else {
                x = MAX(0, width - textWidth);
            }
        }
        [string drawAtPoint: CGPointMake(x, y) forWidth: width withFont: self.font lineBreakMode: UILineBreakModeMiddleTruncation];
        y = floorf(y + (textHeight * self.lineSpacingMuliplier));
    }
}

- (int) currentLines {
    return [[self linesForWidth: self.bounds.size.width].lines count];
}

- (BOOL) ellipsized {
    return [self linesForWidth: self.bounds.size.width].ellipsized;
}

- (CGFloat) widthForText: (NSString *) text {
    return [text sizeWithFont: self.font].width;
}

@end

//MHLabelLines.h
#import <Foundation/Foundation.h>

@interface MHLabelLines : NSObject {
}

@property (nonatomic, retain) NSArray *lines;
@property (nonatomic) BOOL ellipsized;

@end

//MHLabelLines.m
#import "MHLabelLines.h"

@implementation MHLabelLines

@synthesize lines = _lines;
@synthesize ellipsized = _ellipsized;

- (void) dealloc {
    self.lines = nil;
    [super dealloc];
}

@end

//MHLabelLayout.h
#import <Foundation/Foundation.h>
#import "MHLabelLines.h"
#import "MHTextRuler.h"

@interface MHLabelLayout : NSObject {
}

+ (MHLabelLines *) linesForText: (NSString *) text constrainedToLines: (int) maxLines andWidth: (CGFloat) width withRuler: (id<MHTextRuler>) ruler;

@end

//MHLabelLayout.m
#import "MHLabelLayout.h"

@interface MHLabelLayout()

+ (int) lastIndexForString: (NSString *) string thatFits: (CGFloat) width withRuler: (id<MHTextRuler>) ruler; 

@end

@implementation MHLabelLayout

+ (MHLabelLines *) linesForText: (NSString *) text constrainedToLines: (int) maxLines andWidth: (CGFloat) width withRuler: (id<MHTextRuler>) ruler {
    MHLabelLines *labelLines = [[[MHLabelLines alloc] init] autorelease];
    NSMutableArray *lines = [NSMutableArray array];
    NSString *remainingText = text;
    while ([remainingText length] > 0 && (maxLines == 0 || [lines count] < maxLines)) {
        int nextLineLastIndex = [self lastIndexForString: remainingText thatFits: width withRuler: ruler];
        NSString *nextString = [remainingText substringToIndex: nextLineLastIndex];
        remainingText = [remainingText substringFromIndex: MIN(nextLineLastIndex + 1, [remainingText length])];
        if ([lines count] + 1 == maxLines && [remainingText length]) {
            labelLines.ellipsized = YES;
            nextString = [nextString stringByAppendingString: @"..."];
            int ellipsizedIndex = [self lastIndexForString: nextString thatFits: width withRuler: ruler];
            while (ellipsizedIndex + 1 < [nextString length]) {
                nextString = [[nextString substringToIndex: ellipsizedIndex] stringByAppendingString: @"..."];
                ellipsizedIndex = [self lastIndexForString: nextString thatFits: width withRuler: ruler];
            }
        }
        [lines addObject: nextString];
    }
    labelLines.lines = lines;
    return labelLines;
}

+ (int) lastIndexForString: (NSString *) string thatFits: (CGFloat) width withRuler: (id<MHTextRuler>) ruler {
    int index = 0;
    int nextIndex = 0;
    int stringLength = [string length];
    while (index < stringLength) {
        nextIndex = index + 1;
        NSRange searchRange = NSMakeRange(nextIndex, stringLength - nextIndex);
        NSRange foundRange = [string rangeOfString: @" " options: NSLiteralSearch range: searchRange];
        if (foundRange.location == NSNotFound) {
            nextIndex = stringLength;
        } else {
            nextIndex = foundRange.location;
        }

        CGFloat nextStringWidth = [ruler widthForText: [string substringToIndex: nextIndex]];
        if (nextStringWidth > width) {
            if (index == 0) {
                index = nextIndex;
            }
            break;
        } else {
            index = nextIndex;
        }
    }

    NSRange newlineRange = [string rangeOfString: @"\n" options: NSLiteralSearch range: NSMakeRange(0, index)];
    if (newlineRange.location != NSNotFound) {
        index = newlineRange.location;
    }
    return index;
}


@end

//MHTextRuler.h
#import <Foundation/Foundation.h>

@protocol MHTextRuler <NSObject>

- (CGFloat) widthForText: (NSString *) text;

@end

【讨论】:

    猜你喜欢
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-11
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 2015-04-25
    • 2021-01-08
    相关资源
    最近更新 更多