UITextView属性字符串链接无法被VoiceOver单独访问问题咨询
问题原因
UITextView 默认会被系统识别为单个无障碍元素,当你仅通过属性字符串给文本加 .link 标记但未做无障碍适配时,VoiceOver 无法自动拆分内部链接为独立可交互元素,就会出现整段文本被作为单个元素读取、链接无法单独选中交互的问题。
修复步骤
1. 调整UITextView基础无障碍配置
在你的configureTextView方法中添加以下配置,关闭整个textView的单元素模式,允许系统遍历内部子元素:
// 放在配置代码的靠前位置 textView.isAccessibilityElement = false textView.isSelectable = true // 你原有配置保留,不要改成false textView.isEditable = false // 原有配置保留 if #available(iOS 15.0, *) { textView.textInteractionEnabled = true }
注意:如果isAccessibilityElement设为true,系统会把整个UITextView当成单个无障碍元素,永远不会识别内部的独立链接。
2. 给每个链接补全无障碍属性
你当前仅给链接设置了.link属性值,需要额外补充无障碍相关的特质标记,修改每个链接的属性添加代码,以服务条款链接为例:
if let range = Subscription.smsPriceDisclaimer.range(of: Subscription.termsOfUse), let url = URL(string: AppURL.Shop.URL.termsOfUse) { let nsRange = Subscription.smsPriceDisclaimer.nsRange(from: range) attributedString.addAttributes([ .link: url, .accessibilityTraits: UIAccessibilityTraits.link.rawValue, .accessibilityTextualContext: "link" ], range: nsRange) }
剩下的隐私政策、财务激励通知两个链接,按完全相同的逻辑补全两个无障碍属性即可。
3. 低版本兼容(iOS 14及以下)
iOS 14及更早系统不会自动识别属性字符串里的链接为独立无障碍元素,需要手动生成UIAccessibilityElement赋值给textView的accessibilityElements属性,核心逻辑是把整段文本拆分为普通文本段、链接段,分别生成对应特质的无障碍元素,给链接元素绑定点击跳转逻辑,参考代码:
// 放在configureTextView方法最后,attributedText赋值完成后调用 var a11yElements = [Any]() let fullText = Subscription.smsPriceDisclaimer as NSString let linkConfigs: [(text: String, urlStr: String)] = [ (Subscription.termsOfUse, AppURL.Shop.URL.termsOfUse), (Subscription.privacyPolicy, AppURL.Shop.URL.policiesAndDisclosures), (Subscription.noticeOfFinancialIncentive, AppURL.Shop.URL.noticeOfFinancialIncentive) ] var currentLocation = 0 for config in linkConfigs { let linkRange = fullText.range(of: config.text) guard linkRange.location != NSNotFound else { continue } // 添加上一段普通文本 if linkRange.location > currentLocation { let plainRange = NSRange(location: currentLocation, length: linkRange.location - currentLocation) let plainElement = UIAccessibilityElement(accessibilityContainer: textView) plainElement.accessibilityLabel = fullText.substring(with: plainRange) plainElement.accessibilityTraits = .staticText plainElement.accessibilityFrameInContainerSpace = textView.layoutManager.boundingRect(forGlyphRange: plainRange, in: textView.textContainer) a11yElements.append(plainElement) } // 添加链接元素 let linkElement = UIAccessibilityElement(accessibilityContainer: textView) linkElement.accessibilityLabel = config.text linkElement.accessibilityTraits = .link linkElement.accessibilityHint = "双击打开页面" linkElement.accessibilityFrameInContainerSpace = textView.layoutManager.boundingRect(forGlyphRange: linkRange, in: textView.textContainer) linkElement.accessibilityActivateBlock = { [weak self] in guard let url = URL(string: config.urlStr) else { return } self?.delegate?.showWebview(urlString: "\(url)", titleValue: config.text) } a11yElements.append(linkElement) currentLocation = linkRange.location + linkRange.length } // 添加末尾剩余的普通文本 if currentLocation < fullText.length { let endRange = NSRange(location: currentLocation, length: fullText.length - currentLocation) let endElement = UIAccessibilityElement(accessibilityContainer: textView) endElement.accessibilityLabel = fullText.substring(with: endRange) endElement.accessibilityTraits = .staticText endElement.accessibilityFrameInContainerSpace = textView.layoutManager.boundingRect(forGlyphRange: endRange, in: textView.textContainer) a11yElements.append(endElement) } textView.accessibilityElements = a11yElements
验证方式
修改完成后打开VoiceOver,单指左右滑动遍历页面元素,选中链接时VoiceOver会朗读“[链接文本],链接,双击打开”,双击即可触发跳转逻辑,说明适配完成。
内容的提问来源于stack exchange,提问作者Akash Singh
相关产品推荐
相关产品推荐

