xcode 为NSMutableAttributedString启用孤立词功能

jbose2ul  于 2022-11-18  发布在  其他
关注(0)|答案(3)|浏览(237)

UILabel的默认行为是防止孤立词单独出现在单独的行中。例如:如果自动换行碰巧在最后一行只保留了一个单词,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标签不会为我工作。

q35jwt9p

q35jwt9p1#

根据OP的评论...
问题不在于属性化文本,因为“普通”文本也会发生同样的情况。
在iOS 11(可能是10)中,Apple更改了UIKit,以防止UILabel换行到***两行文本***时出现孤立。超过两行时仍允许出现孤立:

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上测试一下,如果是这样的话,你可能需要做一个版本检查,以确定是添加一个普通空格还是一个非间断空格。

llew8vvj

llew8vvj2#

来自UILabelthe lineBreakStrategy property的文档,它有助于控制此行为:
当标签具有属性化字符串值时,系统将忽略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 * 可能 * 也会有所帮助,因为这可能允许文本系统在字符串的最后一行收紧单词之间的间距,以使所有内容都适合。(我说 * 可能 * 是因为该属性专门控制截断,但文本系统也可能在换行时考虑到这一点。)

q1qsirdb

q1qsirdb3#

这是苹果自iOS 11以来的一个变化(由@DonMag回答),以防止最后一行出现孤立词。
如果您的制作只支持iOS13.0+,设置lineBreakStrategy将把它设置回旧样式。

let label = UILabel()
if #available(iOS 14.0, *) {
    label.lineBreakStrategy = NSParagraphStyle.LineBreakStrategy()
}

(One有趣的是,我发现这个lineBreakStrategy也适用于iOS 13. 0+,即使从Apple's document它提到了iOS 14. 0+。)
如果你需要支持旧的iOS版本,你需要设置NSAllowsDefaultLineBreakStrategy键的值时,应用程序启动,我找不到任何文件关于它.我测试它在iOS 11 & 12工作,但在iOS 13.0+.

// Setting the undocumented key NSAllowsDefaultLineBreakStrategy
UserDefaults.standard.set(false, forKey: "NSAllowsDefaultLineBreakStrategy")

所以如果你的应用程序支持iOS 11. 0+,你可能需要这两个。希望它能有所帮助;)

相关问题