iOS 11 中的 UILabel 错误换行
UILabel wrong word wrap in iOS 11
我在使用没有自动布局的 XIB 的应用程序时遇到问题。我不知道这是否是重要信息。
我有 2 行使用自动换行的 UILabel。在 iOS 10 中自动换行正常,第一行包含一个单词 + 特殊字符,例如 & 符号。示例:
然后在 iOS 11 处自动换行出现某种错误,将 & 符号放在第二行:
这是有问题的,因为通常放在第二行的较长单词现在无法正确显示。知道发生了什么变化吗?我知道 safeArea 但它看起来不像是理由。任何想法如何将那个符号移动到顶部有很多 space 的地方?
其余设置:
似乎用不间断的 space (U+00A0) 替换符号之前的 space 使符号保持在同一行。根据您为标签生成文本的方式,这可能不容易实现自动化(在某些情况下您可能确实需要将 & 号放在第二行)。
这不是真正的答案,但我想补充说明这是一个普遍问题,与&符号完全无关。
这两个 UILabel 具有相同的宽度限制,并且文本几乎相同。但是第二个有我期望的自动换行。第一个不正确,"about"明明可以留在第一行。
这是 Apple 为防止寡行而做出的更改。从设计的角度来看,最好避免在一行文本中出现一个单词。 UILabel 现在以第二行文本始终至少有 2 个单词的方式换行。
请参阅下面的答案以了解禁用它的选项。
这里还有 a good article 关于“孤儿”和“寡妇”的文字。
使用参数 -NSAllowsDefaultLineBreakStrategy NO
(未记录的默认设置)启动应用程序似乎强制回到旧行为。或者,您可以在启动时将 NSUserDefaults
中的 NSAllowsDefaultLineBreakStrategy
设置为 NO
(当 UILabel
或字符串绘制代码为似乎已初始化,因此您需要在此之后注册一个覆盖值,或者将其插入 NSArgumentDomain
,或者只是永久设置默认值)。
Apple 可能会认为私有 API 并拒绝使用它的应用程序;我不确定。我没有在运输应用程序中尝试过这个。但是,它在快速测试中确实有效——看到 NSUserDefaults
中的设置并发现更改它会改变行为。
一个选项可能是使用 UITextView 来代替——它似乎没有这种行为。如果您将 NSTextContainer.lineFragmentPadding 设置为 0,将 textContainerInset 设置为 UIEdgeInsetsZero,并关闭所有滚动(scrollEnabled、弹跳、滚动指示器等),它将以类似于 UILabel 的方式显示,但没有那么多的约束灵活性。它不是 drop-in 的替代品,但在某些情况下它是可以接受的。
作为一个简单的(hacky)解决方法,您通常可以通过在文本末尾添加空格来使用 UILabel 获得正确的行为。使用您的示例:
包装新的(不需要的)方式:
"oneText & two."
以旧方式包装:
"oneText & two. "
(注意字符串末尾的 2 个额外空格)
明显的缺点是如果这些额外的空格自己被迫换行,但对于像标题这样简单的东西来说通常就足够了。
有点 hack,但您可以在字符串的末尾添加一些零宽度空格以恢复旧行为,而不会影响字符串的布局,否则您会从普通空格获得:
let zeroWidthSpace: Character = "\u{200B}"
let spacingForWordWrapping = String(repeating: zeroWidthSpace, count: 6)
label.text = "oneText & two" + spacingForWordWrapping
自 iOS 14 起,您可以使用 UILabel
实例的 lineBreakStrategy
属性 来控制此行为。
可用值为:
NSParagraphStyle.LineBreakStrategy() // none
NSParagraphStyle.LineBreakStrategy.pushOut
NSParagraphStyle.LineBreakStrategy.hangulWordPriority
NSParagraphStyle.LineBreakStrategy.standard
禁用此行为使用Swift:
if #available(iOS 14.0, *) {
label.lineBreakStrategy = []
}
// Alternatives
// label.lineBreakStrategy = NSParagraphStyle.LineBreakStrategy()
// label.lineBreakStrategy = .init(rawValue: 0)
// label.lineBreakStrategy = .init()
要使其在 较低的 iOS 版本上运行,您可以使用 NSAttributedString
:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakStrategy = []
let attributedString = NSAttributedString(string: "Your text here", attributes: [
.paragraphStyle: paragraphStyle
])
let label = UILabel()
label.attributedText = attributedString
Objective-C:
if (@available(iOS 14.0, *)) {
label.lineBreakStrategy = NSLineBreakStrategyNone;
}
我在使用没有自动布局的 XIB 的应用程序时遇到问题。我不知道这是否是重要信息。
我有 2 行使用自动换行的 UILabel。在 iOS 10 中自动换行正常,第一行包含一个单词 + 特殊字符,例如 & 符号。示例:
然后在 iOS 11 处自动换行出现某种错误,将 & 符号放在第二行:
这是有问题的,因为通常放在第二行的较长单词现在无法正确显示。知道发生了什么变化吗?我知道 safeArea 但它看起来不像是理由。任何想法如何将那个符号移动到顶部有很多 space 的地方?
其余设置:
似乎用不间断的 space (U+00A0) 替换符号之前的 space 使符号保持在同一行。根据您为标签生成文本的方式,这可能不容易实现自动化(在某些情况下您可能确实需要将 & 号放在第二行)。
这不是真正的答案,但我想补充说明这是一个普遍问题,与&符号完全无关。
这两个 UILabel 具有相同的宽度限制,并且文本几乎相同。但是第二个有我期望的自动换行。第一个不正确,"about"明明可以留在第一行。
这是 Apple 为防止寡行而做出的更改。从设计的角度来看,最好避免在一行文本中出现一个单词。 UILabel 现在以第二行文本始终至少有 2 个单词的方式换行。
请参阅下面的答案以了解禁用它的选项。
这里还有 a good article 关于“孤儿”和“寡妇”的文字。
使用参数 -NSAllowsDefaultLineBreakStrategy NO
(未记录的默认设置)启动应用程序似乎强制回到旧行为。或者,您可以在启动时将 NSUserDefaults
中的 NSAllowsDefaultLineBreakStrategy
设置为 NO
(当 UILabel
或字符串绘制代码为似乎已初始化,因此您需要在此之后注册一个覆盖值,或者将其插入 NSArgumentDomain
,或者只是永久设置默认值)。
Apple 可能会认为私有 API 并拒绝使用它的应用程序;我不确定。我没有在运输应用程序中尝试过这个。但是,它在快速测试中确实有效——看到 NSUserDefaults
中的设置并发现更改它会改变行为。
一个选项可能是使用 UITextView 来代替——它似乎没有这种行为。如果您将 NSTextContainer.lineFragmentPadding 设置为 0,将 textContainerInset 设置为 UIEdgeInsetsZero,并关闭所有滚动(scrollEnabled、弹跳、滚动指示器等),它将以类似于 UILabel 的方式显示,但没有那么多的约束灵活性。它不是 drop-in 的替代品,但在某些情况下它是可以接受的。
作为一个简单的(hacky)解决方法,您通常可以通过在文本末尾添加空格来使用 UILabel 获得正确的行为。使用您的示例:
包装新的(不需要的)方式:
"oneText & two."
以旧方式包装:
"oneText & two. "
(注意字符串末尾的 2 个额外空格)
明显的缺点是如果这些额外的空格自己被迫换行,但对于像标题这样简单的东西来说通常就足够了。
有点 hack,但您可以在字符串的末尾添加一些零宽度空格以恢复旧行为,而不会影响字符串的布局,否则您会从普通空格获得:
let zeroWidthSpace: Character = "\u{200B}"
let spacingForWordWrapping = String(repeating: zeroWidthSpace, count: 6)
label.text = "oneText & two" + spacingForWordWrapping
自 iOS 14 起,您可以使用 UILabel
实例的 lineBreakStrategy
属性 来控制此行为。
可用值为:
NSParagraphStyle.LineBreakStrategy() // none
NSParagraphStyle.LineBreakStrategy.pushOut
NSParagraphStyle.LineBreakStrategy.hangulWordPriority
NSParagraphStyle.LineBreakStrategy.standard
禁用此行为使用Swift:
if #available(iOS 14.0, *) {
label.lineBreakStrategy = []
}
// Alternatives
// label.lineBreakStrategy = NSParagraphStyle.LineBreakStrategy()
// label.lineBreakStrategy = .init(rawValue: 0)
// label.lineBreakStrategy = .init()
要使其在 较低的 iOS 版本上运行,您可以使用 NSAttributedString
:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakStrategy = []
let attributedString = NSAttributedString(string: "Your text here", attributes: [
.paragraphStyle: paragraphStyle
])
let label = UILabel()
label.attributedText = attributedString
Objective-C:
if (@available(iOS 14.0, *)) {
label.lineBreakStrategy = NSLineBreakStrategyNone;
}