[...] 当我这样做并尝试对其使用 VoiceOver 时,我最终会阅读整个字符串,而不仅仅是屏幕上的内容 [...] 旁白会阅读整个字符串。
正如我在评论中所说,截断仅用于显示。
VoiceOver 将始终读出字符串的整个文本,它不关心屏幕上的内容。
它与accessibilityLabel 完全一样,可能与显示的不同:这里,accessibilityLabel 是整个字符串内容。 ?
有谁知道如何让它在截断三个点处停止?
你的问题引起了我的好奇心,我决定研究这个问题。
我发现了一个使用 TextKit 的解决方案,其基础知识应该是已知的:如果不是这样的话⟹Apple doc?
⚠️ 主要思想是确定最后一个可见字符的索引,以便提取显示的子字符串并将其分配给最后的accessibilityLabel。 ⚠️
初始假设:使用 UILabel 使用与问题中定义的 lineBreakMode 相同的@(byTruncationTail)。
我在操场上编写了以下整个代码,并使用 Xcode 空白项目确认结果以进行检查。?
import Foundation
import UIKit
extension UILabel {
var displayedLines: (number: Int?, lastIndex: Int?) {
guard let text = text else {
return (nil, nil)
}
let attributes: [NSAttributedString.Key: UIFont] = [.font:font]
let attributedString = NSAttributedString(string: text,
attributes: attributes)
//Beginning of the TextKit basics...
let textStorage = NSTextStorage(attributedString: attributedString)
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainerSize = CGSize(width: frame.size.width,
height: CGFloat.greatestFiniteMagnitude)
let textContainer = NSTextContainer(size: textContainerSize)
textContainer.lineFragmentPadding = 0.0 //Crucial to get the most accurate results.
textContainer.lineBreakMode = .byTruncatingTail
layoutManager.addTextContainer(textContainer)
//... --> end of the TextKit basics
var glyphRangeMax = NSRange()
let characterRange = NSMakeRange(0, attributedString.length)
//The 'glyphRangeMax' variable will store the appropriate value thanks to the following method.
layoutManager.glyphRange(forCharacterRange: characterRange,
actualCharacterRange: &glyphRangeMax)
print("line : sentence -> last word")
print("----------------------------")
var truncationRange = NSRange()
var fragmentNumber = ((self.numberOfLines == 0) ? 1 : 0)
var globalHeight: CGFloat = 0.0
//Each line fragment of the layout manager is enumerated until the truncation is found.
layoutManager.enumerateLineFragments(forGlyphRange: glyphRangeMax) { rect, usedRect, textContainer, glyphRange, stop in
globalHeight += rect.size.height
if (self.numberOfLines == 0) {
if (globalHeight > self.frame.size.height) {
print("⚠️ Constraint ⟹ height of the label ⚠️")
stop.pointee = true //Stops the enumeration and returns the results immediately.
}
} else {
if (fragmentNumber == self.numberOfLines) {
print("⚠️ Constraint ⟹ number of lines ⚠️")
stop.pointee = true
}
}
if (stop.pointee.boolValue == false) {
fragmentNumber += 1
truncationRange = NSRange()
layoutManager.characterRange(forGlyphRange: NSMakeRange(glyphRange.location, glyphRange.length),
actualGlyphRange: &truncationRange)
let sentenceInFragment = self.sentenceIn(truncationRange)
let line = (self.numberOfLines == 0) ? (fragmentNumber - 1) : fragmentNumber
print("\(line) : \(sentenceInFragment) -> \(lastWordIn(sentenceInFragment))")
}
}
let lines = ((self.numberOfLines == 0) ? (fragmentNumber - 1) : fragmentNumber)
return (lines, (truncationRange.location + truncationRange.length - 2))
}
//Function to get the sentence of a line fragment
func sentenceIn(_ range: NSRange) -> String {
var extractedString = String()
if let text = self.text {
let indexB = text.index(text.startIndex,
offsetBy:range.location)
let indexF = text.index(text.startIndex,
offsetBy:range.location+range.length)
extractedString = String(text[indexB..<indexF])
}
return extractedString
}
}
//Function to get the last word of the line fragment
func lastWordIn(_ sentence: String) -> String {
var words = sentence.components(separatedBy: " ")
words.removeAll(where: { $0 == "" })
return (words.last == nil) ? "o_O_o" : (words.last)!
}
完成后,我们需要创建标签:
let labelFrame = CGRect(x: 20,
y: 50,
width: 150,
height: 100)
var testLabel = UILabel(frame: labelFrame)
testLabel.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
...并测试代码:
testLabel.numberOfLines = 3
if let _ = testLabel.text {
let range = NSMakeRange(0,testLabel.displayedLines.lastIndex! + 1)
print("\nNew accessibility label to be implemented ⟹ \"\ (testLabel.sentenceIn(range))\"")
}
playground 的 Debug Area 用 3 行显示结果:
...如果打算使用“尽可能多的行”,我们会得到:
这似乎工作得很好。 ?
在您自己的代码中包含此基本原理,您将能够让 VoiceOver 读取屏幕上显示的标签的截断内容。 ??