【问题标题】:UIButton's Title Label Word Wrap with Tail TruncationUIButton 的标题标签自动换行与尾部截断
【发布时间】:2011-09-11 12:52:45
【问题描述】:

我需要同时在UIButtontitleLabel 上启用自动换行和尾部截断。将 numberOfLines 设置为大于 0 的值不起作用,文本保持在一行。

我已经四处寻找,但没有找到解决方案。有什么想法吗?

【问题讨论】:

  • @Marek 这如何解决我的问题?
  • UILineBreakModeTailTruncation?从行尾截断文本(根据需要)。对于多行文本,仅截断最后一行的文本。
  • 是的,但多行只有在我手动添加换行符时才有效,对吗?基本上我有一个长字符串,需要在多行上显示它(自动换行),如果它比标签长(尾部截断),则截断它。
  • @pt2ph8 不,您不需要手动添加换行符。 UILabel 中的文本将自动换行。

标签: ios uilabel


【解决方案1】:

这是正确的:

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 进行逐行布局。

【讨论】:

  • 这很有帮助,但是,我相信您声称“标签上的 numberOfLines 属性必须设置为 0”的说法是不正确的。您在上面引用的文档以及我自己在 iOS 7.0.3 上的测试都表明您可以将 numberOfLines 设置为(比如说)3,并将换行模式设置为 NSLineBreakByTruncatingTail,并且您将(最多)得到那么多行加上尾部截断。
【解决方案2】:

我在发布此问题的同一天解决了这个问题,将UIButton 放在UILabel 之上,numberOfLines 设置为 3。我没有接受这个问题,看看是否有人有更好的主意,但显然没有其他解决方案。

【讨论】:

  • 在 iOS 6 上你可以这样做:label.lineBreakMode = NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail;
  • @Jeff - 不,你不能那样做。查看NSLineBreakMode 的类型,您就会明白为什么。 (或者上面的答案)
【解决方案3】:
[self.costomButton.titleLabel setTextAlignment:UITextAlignmentLeft];
[self.costomButton.titleLabel setNumberOfLines:3];

请务必先设置Alignment ps:只有系统版本大于5.0才有效

【讨论】:

  • 设置AlignmentLeft 为我工作。谢谢大佬。
【解决方案4】:

尝试将 numberOfLines 设置为大于 2,并相应地设置高度。

    m_button = [UIButton buttonWithType:UIButtonTypeCustom];
[m_button setFrame:CGRectMake(isLandscape?20:10, 40, isLandscape?300:250, 40)];
m_button.titleLabel.font  = [UIFont fontWithName:@"HelveticaNeue" size:17];
[m_btnDiscoverPoint setTitle:@"Title" forState:UIControlStateNormal];
CGRect buttonFrame = [m_button frame];

if ([m_button.titleLabel.text length]>0) {
    CGSize suggestedSize = [m_button.titleLabel.text sizeWithFont:[UIFont fontWithName:@"HelveticaNeue" size:17] constrainedToSize:CGSizeMake(FLT_MAX,m_button.frame.size.height) lineBreakMode:UILineBreakModeWordWrap];

    if (suggestedSize.width >= self.view.frame.size.width) {
        suggestedSize.width = self.view.frame.size.width-10;
        suggestedSize.height=suggestedSize.height+20;
        m_button.titleLabel.numberOfLines=2;
    }
    else{
        m_button.titleLabel.numberOfLines=1;
    }

    buttonFrame.size.width = suggestedSize.width;

    [m_button setFrame:buttonFrame];
}
[m_button setBackgroundColor:[UIColor clearColor]];
[m_button addTarget:self action:@selector(btnClickAction) forControlEvents:UIControlEventTouchUpInside];

【讨论】:

    【解决方案5】:
    button.titleLabel.numberOfLines = 2;
    button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    UIFont * theFont = [UIFont systemFontOfSize: 14]; // you set
    CGSize textSize = [titleStr sizeWithAttributes:@{NSFontAttributeName: theFont}];
    CGFloat theWidth = kScreenWidth-otherWidthYouSet;// I thought the button's frame is content driving ,and is limited 
    CGFloat ratio = theWidth*heightYouSet/((textSize.width+4)*(textSize.height+6));// 4 , 6 , is made by experience . I think the textSize is taken one line text default by the system 
    NSUInteger validNum = ratio * titleStr.length;
    
    if(ratio<1){
        [button setTitle: [[titleStr substringToIndex: validNum] stringByAppendingString: @"..."] state: yourState];
    
    }
    else{
        [button setTitle: titleStr state: yourState];
    }
    

    【讨论】:

      【解决方案6】:

      斯威夫特 5:

      只需将“numberOfLines = 2”和“lineBreakMode”设置为“truncate tail”到您的UILabel,您就可以拥有多行以及尾部截断。

      【讨论】:

        【解决方案7】:

        您可以使用按位或运算符在标签上指定多个 lineBreakMode。

        例如,以下代码将包装标签的文本,并在文本扩展超出标签框架高度的大小时在文本的尾部添加省略号。

        lblTemp.lineBreakMode = UILineBreakModeWordWrap | UILineBreakModeTailTruncation;
        lblTemp.numberOfLines = 0;
        

        更新:这是不正确的。它似乎有效,因为 UILineBreakModeWordWrap 在枚举中为 0。请参阅下面的 cmets。

        【讨论】:

        • 这个解决方案可能没有解决原来的问题,但我的问题听起来像这个问题,这个解决方案解决了我的问题......困惑了吗?无论如何,好一个!
        • 哇,谁知道这支持按位或运算符。我认为 IB 只允许您选择一个选项并没有什么帮助。
        • 这完全工作。只需查看枚举定义即可了解原因。但是它似乎有效,因为UILineBreakModeWordWrap0,所以你实际上在做的事情与lblTemp.lineBreakMode = UILineBreakModeTailTruncation 相同。这对于多行标签实际上是有效的(根据标题中的注释)。
        • @mattjgalloway 你说的很对。啊。当像这样的真正错误的答案得到如此高的投票时,这太可怕了。
        【解决方案8】:

        iOS 中不推荐使用所有 UI 属性使用 NS 缩写而不是 UI。如这里所示的示例 - NSLineBreakByTruncatingMiddle

        【讨论】:

          猜你喜欢
          • 2018-04-20
          • 1970-01-01
          • 1970-01-01
          • 2021-11-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多