[...] 当我这样做并尝试在其上使用VoiceOver时,它最终会读取整个字符串,而不仅仅是屏幕中的内容[...] VoiceOver会读取整个字符串。
正如我在评论中所说,
截断仅用于显示。
VoiceOver始终会朗读出字符串的整个文本,并且不关心屏幕上显示的内容。
这就像
accessibilityLabel
可能与显示的内容不同一样:在这里,accessibilityLabel是整个字符串内容。
有人知道如何使它停留在省略号处吗?
你的问题引起了我的好奇心,我决定研究一下这个问题。
我找到了一个
使用TextKit的解决方案,其基础应该是已知的:如果不是这种情况 ⟹
Apple doc
⚠️ 主要思路是确定最后一个可见字符的索引,以便提取显示的子字符串并将其分配给accessibilityLabel。 ⚠️
初始假设:使用与问题中定义的lineBreakMode
相同的UILabel (byTruncationTail)。
我在playground中编写了以下所有代码,并使用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)
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
textContainer.lineBreakMode = .byTruncatingTail
layoutManager.addTextContainer(textContainer)
var glyphRangeMax = NSRange()
let characterRange = NSMakeRange(0, attributedString.length)
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
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
}
} 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))
}
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
}
}
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))\"")
}
游乐场的调试区域显示3行结果:
...如果想要“尽可能多的行”,我们会得到:
这似乎很有效。
将此原理包含在您自己的代码中,您将能够使VoiceOver读取屏幕上显示的标签的截断内容。
string.accessibilityHint = "Lorem ipsum dolor sit amet short"
吗? - Dmytro RostopiraaccessibilityLabel
中的字符串。这适用于所有断言技术,而不仅仅是 VoiceOver。你在问题中提到截断会随着设备大小而改变,你应该将 VoiceOver 视为一种可以“显示”任意长度字符串的设备。 - Richard StellingaccessibilityLabel
...这似乎很有趣,不是吗?所有用户将拥有与屏幕上相同的信息。 - XLE_22