iOS自动换行更均匀?

时间:2012-07-31 21:08:09

标签: ios uilabel word-wrap

我有一个标题,我放入UILabel。我得到它将文本很好地包装到两行,但通常第二行只有一个小字。有没有办法让系统更均匀地包裹线条?

标题是动态提取的,所以我不相信我可以提前确定。

4 个答案:

答案 0 :(得分:1)

您可以计算单行上的长度,然后取该宽度除以2,并将该结果用作2行标签的宽度约束。这可能会让你获得更好的视觉效果,但是如果你的标签长到3行(除非超过2行就可以),你可能需要稍微调整一下。

这种方法的缺点是可以做一些额外的工作,但它可能会成功。例如:

    NSString *text = @"This is my very long title I want evenly on 2 lines";
    UILabel *theLabel = [[UILabel alloc] init];
    theLabel.text = text;
    // ...other label setup here, like font, etc
    [theLabel sizeToFit];

    CGSize constraint = CGSizeMake(theLabel.frame.size.width / 2.0, 1000);
    CGSize labelSize = [theLabel.text sizeWithFont:theLabel.font constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

    theLabel.numberOfLines = 0;
    theLabel.lineBreakMode = UILineBreakModeWordWrap;
    CGRect frame = theLabel.frame;
    frame.size = labelSize;
    theLabel.frame = frame;

应该这样做。警告:从内存写。 : - )

答案 1 :(得分:1)

我不相信有这么简单的方法。向NSString添加了UIKit,允许您确定显示具有特定字体的字符串所需的尺寸。您可以做的是使用所有可用宽度计算所需的高度,然后在循环中运行相同的计算,减小宽度,直到它需要额外的垂直空间。然后使用前一个值作为标签的宽度并适当对齐。

答案 2 :(得分:1)

我继续创造了一个名为EvenlyWrappedLabel的CocoaPod,其灵感来自Erik van der Neut的回答。它有一些简洁的功能:

  1. 防止单个单身孤儿占据整个底线。

    示例:

    This sentence has a single
    orphan.
    

    变为:

    This sentence has
    a single orphan.
    
  2. 防止文字在顶部分组,感觉头重脚轻。

    示例:

    This sentence has a lot of words on
    the top line.
    

    变为:

    This sentence has a lot
    of words on the top line.
    
  3. 可选择在每条可用线上均匀分布文字。

    示例:

    This only takes up one line.
    

    变为*:

    This only
    takes up
    one line.
    

    * numberOfLines = 3useEveryLine = true

  4. 使用autolayout约束并尊重内在大小。

  5. 非常容易整合,只需将let label = UILabel()替换为let label = EvenlyWrappedLabel()

  6. 一个糟糕的屁股示例应用。

  7. Example Project Screenshot

    如果您不使用CocoaPods,可以在GitHub上找到源代码。享受!

答案 3 :(得分:0)

对于多语言应用程序,我也需要修复同样的问题。该应用程序可以用9种不同的语言显示其UI,并且语言之间的消息长度变化很大。因此,我最终为一些多行消息提供了一些不幸的默认自动换行。例如,有几个例子,日语翻译最后有一整行文字,后面只有一两个字符。

我在Layout Utility类中添加了以下两个方法,为我解决了这个问题(参见下面的代码)。此解决方案适用于具有任意数量的行(1,2,3,更多......)和任何语言的标签。 (将ECLayoutUtility更改为您自己的班级名称。)

基本理念是:

  1. 如果UILabel显示文本,请检测其当前高度。

  2. 二进制搜索该标签的最小宽度,增加该高度。

  3. 在下面的代码中,您可以通过while循环使用granularity变量控制精度与转弯次数之间的权衡。

    此修复之前有问题的日语标签示例:

    enter image description here

    修复后的那个日本标签:

    enter image description here

    这是我添加到Layout Utility类的两种方法。我将它们拆分为这样,所以你也可以查询获得新的大小,以防你使用Autolayout,并希望使用大小信息来操纵约束而不是直接调整标签本身:

    + (void)wordWrapEvenlyTextInUILabel:(UILabel *)uiLabel
                            forMaxWidth:(CGFloat)maxLabelWidth
    {
        [ECLayoutUtility resizeView:uiLabel
                             toSize:[self getLabelSizeWithMinimumWidthForLabel:uiLabel
                                                                   forMaxWidth:maxLabelWidth]];
    }
    
    
    + (CGSize)getLabelSizeWithMinimumWidthForLabel:(UILabel *)uiLabel
                                       forMaxWidth:(CGFloat)maxLabelWidth
    {
        NSLog(@"Current size for label \"%@\" is %@", uiLabel.text, NSStringFromCGSize(uiLabel.frame.size));
    
        // Start off by determining what height the label would get for the given maximum width:
        CGSize labelSize = [uiLabel.text boundingRectWithSize:CGSizeMake(maxLabelWidth, MAXFLOAT)
                                                      options:NSStringDrawingUsesLineFragmentOrigin
                                                   attributes:@{ NSFontAttributeName:uiLabel.font }
                                                      context:nil].size;
    
        // Constraining the label to that height, now figure out the mimumum width for
        // which this label still shows its text within that given height. We find
        // that height with a binary search for the smallest label width that does
        // not increment the label height.
    
        CGFloat maxWidth    = maxLabelWidth;
        CGFloat testWidth   = 0.5 * maxWidth;
        CGFloat minWidth    = 0;
        CGFloat granularity = 1;
    
        while (maxWidth > testWidth + granularity)
        {
            CGFloat newHeight = [uiLabel.text boundingRectWithSize:CGSizeMake(testWidth, MAXFLOAT)
                                                           options:NSStringDrawingUsesLineFragmentOrigin
                                                        attributes:@{ NSFontAttributeName:uiLabel.font }
                                                           context:nil].size.height;
    
            NSLog(@"H: %.2f, min W: %.2f, mid W: %.2f, max W: %.2f", newHeight, minWidth, testWidth, maxWidth);
    
            if (newHeight > labelSize.height)
            {
                // Width reduction lead to height increase, so new width is too small.
                // Now set the new width to mid-point between what we just tried and
                // the last known acceptable width:
                minWidth   = testWidth;                         // increase the lower bound
                testWidth += 0.5 * (maxWidth - testWidth);      // increase width for next test
            }
            else
            {
                // Height not affected by the width reduction, so lets try to reduce it even more:
                maxWidth   = testWidth;                         // reduce the upper bound
                testWidth -= 0.5 * (testWidth - minWidth);      // reduce width for next test
            }
        }
    
        CGSize newSize = CGSizeMake(maxWidth, labelSize.height);
        NSLog(@"New size for label \"%@\" is %@", uiLabel.text, NSStringFromCGSize(newSize));
    
        return newSize;
    }
    

    我在View Controller中的+wordWrapEvenlyTextInUILabel:forMaxWidth:调用-viewWillLayoutSubviews方法,如下所示:

    // Make sure that any text in the promo message that is wrapped over multiple lines
    // is wrapped evenly, so we don't get very uneven lines that look ugly:
    [ECLayoutUtility wordWrapEvenlyTextInUILabel:_promoMessageLabel
                                     forMaxWidth:_promoMessageLabel.frame.size.width];
    

    我已经针对不同语言的不同标签对其进行了测试,看起来它完美地完成了这项工作。这是一个越南的例子:

    enter image description here

    我希望这也有助于其他人: - )

    谢谢,

    埃里克