繁体   English   中英

为 NSMutableAttributedString 启用孤词功能

[英]Enable orphan words feature for NSMutableAttributedString

UILabel 的默认行为是它防止孤立词单独出现在单独的行上。 即:如果自动换行恰好在最后一行保留 1 个单词。 iOS 将通过从前一行发送一个单词来防止这种情况发生,在最后一行有两个单词。

问题是此功能默认情况下不适用于NSMutableAttributedString 我该如何启用它?

样本:

var string = customField?.title ?? ""
    
if customField?.required == true {
    string += " *"
} else {
    string += " (\(getLocalizedString(localizedKey: .optional)))"
}
            
let style = NSMutableParagraphStyle()
if #available(iOS 14.0, *) {
    style.lineBreakStrategy = .standard
}

let att = NSMutableAttributedString(string: string, attributes: [.paragraphStyle: style])
    
titleLabel.attributedText = att

请记住,由于其他原因,我被迫使用NSMutableAttributedString 2个标签对我不起作用。

在此处输入图像描述

根据OP的评论...

问题不在于属性文本,因为“普通”文本也会发生同样的事情。

在 iOS 11(可能是 10)中,Apple 更改了 UIKit 以防止UILabel行为两行 text时出现孤儿。 仍然允许使用多于两行的孤儿:

在此处输入图像描述

A在 iOS 11 之前... B是当前... C是当前有超过两行...

请注意D示例——我没有安装 Xcode 测试版,但根据我看到的其他评论,似乎在iOS 16中,当文本换行超过两行时,也会应用“无孤儿”规则.

所以......解决您的问题的一种方法是在最后一个单词和星号之间使用“非换行符”字符(而不是纯空格)。

这是一个快速测试:

class WrapTestVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.spacing = 4
        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)

        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            stackView.widthAnchor.constraint(equalToConstant: 320.0),
        ])
        
        var noteLabel: UILabel!
        var testLabel: UILabel!
    
        let noteFont: UIFont = .systemFont(ofSize: 14.0)
        
        noteLabel = UILabel()
        noteLabel.font = noteFont
        noteLabel.numberOfLines = 0
        noteLabel.text = "Just enough to fit:"
    
        stackView.addArrangedSubview(noteLabel)
        
        testLabel = UILabel()
        testLabel.backgroundColor = .yellow
        testLabel.numberOfLines = 0
        testLabel.attributedText = sampleAttrString(method: 0)

        stackView.addArrangedSubview(testLabel)
        
        stackView.setCustomSpacing(20.0, after: testLabel)
        
        noteLabel = UILabel()
        noteLabel.font = noteFont
        noteLabel.numberOfLines = 0
        noteLabel.text = "Using a space char:"
        
        stackView.addArrangedSubview(noteLabel)
        
        testLabel = UILabel()
        testLabel.backgroundColor = .yellow
        testLabel.numberOfLines = 0
        testLabel.attributedText = sampleAttrString(method: 1)
        
        stackView.addArrangedSubview(testLabel)
        
        stackView.setCustomSpacing(20.0, after: testLabel)
        
        noteLabel = UILabel()
        noteLabel.font = noteFont
        noteLabel.numberOfLines = 0
        noteLabel.text = "Using a non-break-space char:"
        
        stackView.addArrangedSubview(noteLabel)
        
        testLabel = UILabel()
        testLabel.backgroundColor = .yellow
        testLabel.numberOfLines = 0
        testLabel.attributedText = sampleAttrString(method: 2)
        
        stackView.addArrangedSubview(testLabel)

        stackView.setCustomSpacing(20.0, after: testLabel)
        
        noteLabel = UILabel()
        noteLabel.font = noteFont
        noteLabel.numberOfLines = 0
        noteLabel.text = "Although, iOS 16 may give:"
        
        stackView.addArrangedSubview(noteLabel)
        
        testLabel = UILabel()
        testLabel.backgroundColor = .yellow
        testLabel.numberOfLines = 0
        testLabel.attributedText = sampleAttrString(method: 3)
        
        stackView.addArrangedSubview(testLabel)
        
        stackView.setCustomSpacing(20.0, after: testLabel)
        

    }

    func sampleAttrString(method: Int) -> NSMutableAttributedString {
        let fontA: UIFont = .systemFont(ofSize: 20.0, weight: .bold)
        
        let attsA: [NSAttributedString.Key : Any] = [
            .font: fontA,
            .foregroundColor: UIColor.blue,
        ]
        
        let attsB: [NSAttributedString.Key : Any] = [
            .font: fontA,
            .foregroundColor: UIColor.red,
        ]
        
        var partOne = NSMutableAttributedString(string: "If the label has enough text so it wraps to more than two lines, UIKit will allow a last word orphan.", attributes: attsA)
        
        var partTwo: NSAttributedString = NSAttributedString()
        
        switch method {
        case 0:
            ()
        case 1:
            partTwo = NSAttributedString(string: " *", attributes: attsB)
        case 2:
            partTwo = NSAttributedString(string: "\u{a0}*", attributes: attsB)
        case 3:
            partOne = NSMutableAttributedString(string: "If the label has enough text so it wraps to more than two lines, UIKit will allow a last\nword orphan.", attributes: attsA)
            partTwo = NSAttributedString(string: "\u{a0}*", attributes: attsB)
        default:
            ()
        }
        
        partOne.append(partTwo)
        
        return partOne
    }

}

输出:

在此处输入图像描述

所以......您需要使用 iOS 16 进行测试,如果是这种情况,您可能需要进行版本检查以确定是添加纯空格还是非中断空格。

来自UILabellineBreakStrategy属性的文档,它有助于控制这种行为:

当标签具有属性字符串值时,系统会忽略textColorfonttextAlignmentlineBreakModelineBreakStrategy属性。 改为在属性字符串中设置NSForegroundColorAttributeNameNSFontAttributeNamealignmentlineBreakModelineBreakStrategy属性。

如果您想使用特定的换行策略,例如.standard (“文本系统使用与标准 UI 标签相同的换行策略配置。”),您需要将属性应用于属性字符串通过段落样式:

let style = NSMutableParagraphStyle()
style.lineBreakStrategy = .standard

let text = NSMutableAttributedString(
    string: "long title with an asterisk at the end *",
    attributes: [.paragraphStyle: style]
)

titleLabel.attributedText = text

根据您的文本,在段落样式上设置allowsDefaultTighteningForTruncation可能会有所帮助,因为这可能允许文本系统收紧字符串最后一行的单词之间的空间以使所有内容都适合。 (我说可能是因为此属性专门控制截断,但文本系统也有可能将其考虑到换行。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM