这是不正确的:
lblTemp.lineBreakMode = NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail
lblTemp.numberOfLines = 0;
NSLineBreakMode 在 NSParagraphStyle.h 中定义为:
typedef NS_ENUM(NSInteger, NSLineBreakMode) { /* What to do with long lines */
NSLineBreakByWordWrapping = 0, /* Wrap at word boundaries, default */
NSLineBreakByCharWrapping, /* Wrap at character boundaries */
NSLineBreakByClipping, /* Simply clip */
NSLineBreakByTruncatingHead, /* Truncate at head of line: "...wxyz" */
NSLineBreakByTruncatingTail, /* Truncate at tail of line: "abcd..." */
NSLineBreakByTruncatingMiddle /* Truncate middle of line: "ab...yz" */
} NS_ENUM_AVAILABLE_IOS(6_0);
请注意,它是一个 NS_ENUM,而不是一个 NS_OPTION,所以它不能用作掩码。如需更多信息,请参阅this。
实际上,对这些常量使用 | 运算符会导致掩码匹配 NSLineBreakByTruncatingTail:
(NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail) == 4
NSLineBreakByTruncatingTail == 4
据我所知,截断 Core Text 中的最后一行并进行自动换行无法通过简单的 CTFramesetterCreateWithAttributedString 和 CTFrameDraw API 完成,但可以通过逐行布局来完成, UILabel 必须做什么。
iOS 6 通过在 NSStringDrawing.h 中公开新的绘图 API 来简化这一点:
typedef NS_ENUM(NSInteger, NSStringDrawingOptions) {
NSStringDrawingTruncatesLastVisibleLine = 1 << 5, // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set.
NSStringDrawingUsesLineFragmentOrigin = 1 << 0, // The specified origin is the line fragment origin, not the base line origin
NSStringDrawingUsesFontLeading = 1 << 1, // Uses the font leading for calculating line heights
NSStringDrawingUsesDeviceMetrics = 1 << 3, // Uses image glyph bounds instead of typographic bounds
} NS_ENUM_AVAILABLE_IOS(6_0);
@interface NSAttributedString (NSExtendedStringDrawing)
- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
@end
因此,如果您使用 UILabel,您希望将 NSAttributedString 的 NSParagraphStyle 或标签本身的 lineBreakMode 设置为:
NSLineBreakByTruncatingTail
标签上的 numberOfLines 属性必须设置为 0。
来自 numberOfLines 上的 UILabel 标头:
// if the height of the text reaches the # of lines or the height of the view is less than the # of lines allowed, the text will be
// truncated using the line break mode.
来自 UILabel 文档:
This property controls the maximum number of lines to use in order to fit the label’s text into its bounding rectangle. The default value for this property is 1. To remove any maximum limit, and use as many lines as needed, set the value of this property to 0.
If you constrain your text using this property, any text that does not fit within the maximum number of lines and inside the bounding rectangle of the label is truncated using the appropriate line break mode.
UILabel 这个有点晦涩的特性带来的唯一问题是,如果不即时修改 NSAttributedString 的 NSParagraphStyle,就无法在绘制之前获得大小(这对于某些 UITableView + UITableViewCell 动态布局是必需的)。
从 iOS 6.1.4 开始,使用具有 NSLineBreakByTruncatingTail 换行模式(用于 UILabel)的 NSAttributedString 调用 -boundingRectWithSize:options:context,返回不正确的单行高度,即使以下选项传入:
(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine)
(请注意 NSStringDrawingUsesLineFragmentOrigin 是多行字符串的必需品。)
更糟糕的是 UILabel 的 lineBreakMode 确实 不 覆盖 NSAttributedStrings 段落样式,因此您必须修改属性字符串的段落样式以进行大小计算,然后将其传递给 UILabel 以便它可以画出来。
也就是说,NSLineBreakByWordWrapping 用于 -boundingRectWithSize:options:context 和 NSLineBreakByTruncatingTail 用于 UILabel(因此它可以在内部使用 NSStringDrawingTruncatesLastVisibleLine,或者它可以用来剪裁最后一行)
如果您不想多次更改字符串的段落样式,唯一的选择是做一个简单的 UIView 子类,覆盖 -drawRect: (将适当的 contentMode 设置为重绘),并使用 iOS 6新的绘图 API:
- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
记得使用 NSLineBreakByWordWrapping 并传入 (NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine) 作为选项。
最后,在 iOS 6 之前,如果您想对属性字符串进行自动换行 + 尾部截断,您必须自己使用 Core Text 进行逐行布局。