我有一个放入 UILabel 的标题。我让它将文本很好地包装成两行,但通常第二行只有一个小词。有没有办法告诉系统更均匀地换行?
标题是动态获取的,所以我不相信我可以提前确定。
我继续创建了一个名为EvenlyWrappedLabel的 CocoaPod ,其灵感来自 Erik van der Neut 的回答。它有一些简洁的功能:
防止单个单词孤儿占据整个底线。
例子:
This sentence has a single
orphan.
变成:
This sentence has
a single orphan.
防止文本在顶部分组,感觉头重脚轻。
例子:
This sentence has a lot of words on
the top line.
变成:
This sentence has a lot
of words on the top line.
可以在可用的每一行上均匀分布文本的选项。
例子:
This only takes up one line.
变成*:
This only
takes up
one line.
* numberOfLines = 3
,useEveryLine = true
适用于自动布局约束并尊重内在大小。
非常容易集成,只需替换let label = UILabel()
为let label = EvenlyWrappedLabel()
.
一个糟糕的示例应用程序。
如果你不使用 CocoaPods,你可以在GitHub 上找到源代码。享受!
您可以计算出单行的长度,然后取该宽度,除以 2,并将该结果用作 2 行标签的宽度约束。这可能会给你带来更好的视觉效果,但如果你有很长的单词会导致你的标签是 3 行(除非超过 2 行是可以的),你可能需要稍微调整一下。
这种方法的缺点是做一些额外的工作,但它可能会奏效。例如:
NSString *text = @"这是我的很长的标题,我想均匀地放在 2 行上"; UILabel *theLabel = [[UILabel alloc] init]; theLabel.text = 文本; // ...这里的其他标签设置,如字体等 [theLabel sizeToFit]; CGSize 约束 = 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; 框架大小=标签大小; theLabel.frame = 框架;
那应该这样做。警告:凭记忆写。:-)
我不相信有一种简单的方法可以做到这一点。UIKit 的附加功能NSString
允许您确定显示具有特定字体的字符串所需的尺寸。您可以做的是使用所有可用宽度计算必要的高度,然后在循环中运行相同的计算,减小宽度直到它需要额外的垂直空间。然后使用之前的值作为标签的宽度并适当对齐。
对于多语言应用程序,我也需要修复这个完全相同的问题。该应用程序可以用 9 种不同的语言显示其 UI,并且不同语言的消息长度差异很大。正因为如此,我最终对一些多行消息进行了一些不幸的默认自动换行。例如,在某些情况下,日文翻译的结尾是一整行文本,下一行只有一两个字符。
我在我的布局实用程序类中添加了以下两种方法,为我解决了这个问题(见下面的代码)。此解决方案适用于具有任意行数(1、2、3、更多...)和任何语言的标签。(更改ECLayoutUtility
为您自己的班级名称。)
基本思想是这样的:
给定一个显示文本的 UILabel,检测它的当前高度。
对不增加该高度的标签的最小宽度进行二分搜索。
while
在我下面的代码中,您可以使用变量控制精度和通过循环的圈数之间的权衡granularity
。
此修复之前有问题的日语标签示例:
修复后的同一个日本标签:
这是我添加到布局实用程序类中的两种方法。我像这样拆分它们,因此您也可以只查询以获取新大小,以防您使用 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;
}
我+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];
我已经针对不同语言的不同标签对此进行了测试,它看起来完美地完成了这项工作。这是一个越南的例子:
我希望这对其他人也有帮助:-)
谢谢,
埃里克