Skip to content

Commit e7411bd

Browse files
author
Developer
committed
实现 Accessibility - 无障碍功能
1 parent 3e3d1ae commit e7411bd

1 file changed

Lines changed: 184 additions & 0 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
//
2+
// BSTextAccessibility.swift
3+
// BSText 3.0
4+
//
5+
// Accessibility support for BSText components.
6+
//
7+
8+
import UIKit
9+
10+
/// Accessibility extensions for BSTextView.
11+
extension BSTextView {
12+
13+
/// Configures accessibility settings for the text view.
14+
public func configureAccessibility() {
15+
isAccessibilityElement = true
16+
accessibilityLabel = "Text editor"
17+
accessibilityHint = "Double tap to edit"
18+
accessibilityTraits = .staticText
19+
}
20+
21+
/// Updates accessibility label with current text content.
22+
public func updateAccessibilityLabel() {
23+
let textLength = text.count
24+
if textLength > 0 {
25+
let preview = text.prefix(50) + (textLength > 50 ? "..." : "")
26+
accessibilityLabel = "\(preview) (\(textLength) characters)"
27+
} else {
28+
accessibilityLabel = "Empty text editor"
29+
}
30+
}
31+
32+
/// Posts accessibility notification when text changes.
33+
public func postTextChangeNotification() {
34+
UIAccessibility.post(notification: .layoutChanged, argument: self)
35+
}
36+
}
37+
38+
/// Accessibility extensions for BSTextCodeEditor.
39+
extension BSTextCodeEditor {
40+
41+
/// Configures accessibility settings for the code editor.
42+
public override func configureAccessibility() {
43+
isAccessibilityElement = true
44+
accessibilityLabel = "Code editor"
45+
accessibilityHint = "Double tap to edit code"
46+
accessibilityTraits = .staticText
47+
}
48+
}
49+
50+
/// Accessibility container for text attachments.
51+
@objcMembers
52+
public class BSTextAttachmentAccessibilityElement: UIAccessibilityElement {
53+
54+
/// The attachment this element represents.
55+
public weak var attachment: BSTextAttachment?
56+
57+
/// The text view containing this attachment.
58+
public weak var textView: UIView?
59+
60+
public init(accessibilityContainer: Any, attachment: BSTextAttachment, textView: UIView) {
61+
super.init(accessibilityContainer: accessibilityContainer)
62+
self.attachment = attachment
63+
self.textView = textView
64+
configure()
65+
}
66+
67+
private func configure() {
68+
guard let attachment = attachment else {
69+
accessibilityLabel = "Attachment"
70+
accessibilityTraits = .staticText
71+
return
72+
}
73+
74+
switch attachment.attachmentType {
75+
case .image:
76+
accessibilityLabel = "Image attachment"
77+
accessibilityTraits = .image
78+
79+
case .animatedImage:
80+
accessibilityLabel = "Animated image"
81+
accessibilityTraits = .image
82+
83+
case .video:
84+
accessibilityLabel = "Video attachment"
85+
accessibilityTraits = .staticText
86+
87+
case .view:
88+
accessibilityLabel = "View attachment"
89+
accessibilityTraits = .staticText
90+
91+
case .swiftUI:
92+
accessibilityLabel = "SwiftUI attachment"
93+
accessibilityTraits = .staticText
94+
95+
default:
96+
accessibilityLabel = "Attachment"
97+
accessibilityTraits = .staticText
98+
}
99+
}
100+
}
101+
102+
/// Accessibility delegate for BSTextView.
103+
@objc public protocol BSTextAccessibilityDelegate: AnyObject {
104+
105+
/// Called when accessibility element needs to be created for an attachment.
106+
///
107+
/// - Parameters:
108+
/// - textView: The text view.
109+
/// - attachment: The attachment.
110+
/// - frame: The frame of the attachment.
111+
/// - Returns: An accessibility element for the attachment.
112+
@objc optional func textView(_ textView: BSTextView, accessibilityElementFor attachment: BSTextAttachment, frame: CGRect) -> BSTextAttachmentAccessibilityElement?
113+
}
114+
115+
/// Accessibility helper for managing attachment accessibility.
116+
@objcMembers
117+
public class BSTextAccessibilityManager: NSObject {
118+
119+
/// The text view being managed.
120+
public weak var textView: BSTextView?
121+
122+
/// Accessibility elements for attachments.
123+
public private(set) var attachmentElements: [BSTextAttachmentAccessibilityElement] = []
124+
125+
public init(textView: BSTextView) {
126+
self.textView = textView
127+
super.init()
128+
}
129+
130+
/// Updates accessibility elements for current attachments.
131+
public func updateAccessibilityElements() {
132+
attachmentElements.removeAll()
133+
134+
guard let textView = textView else {
135+
return
136+
}
137+
138+
let textStorage = textView.textStorage
139+
textStorage.enumerateAttribute(.attachment, in: NSRange(location: 0, length: textStorage.length)) { value, range, _ in
140+
if let attachment = value as? BSTextAttachment {
141+
let element = BSTextAttachmentAccessibilityElement(
142+
accessibilityContainer: textView,
143+
attachment: attachment,
144+
textView: textView
145+
)
146+
attachmentElements.append(element)
147+
}
148+
}
149+
}
150+
151+
/// Returns all accessibility elements.
152+
public func attachmentAccessibilityElements() -> [Any] {
153+
return attachmentElements
154+
}
155+
}
156+
157+
/// Dynamic Type support extensions.
158+
extension BSTextView {
159+
160+
/// Enables dynamic type support.
161+
public func enableDynamicType() {
162+
NotificationCenter.default.addObserver(
163+
self,
164+
selector: #selector(handleContentSizeCategoryChange),
165+
name: UIContentSizeCategory.didChangeNotification,
166+
object: nil
167+
)
168+
}
169+
170+
/// Disables dynamic type support.
171+
public func disableDynamicType() {
172+
NotificationCenter.default.removeObserver(
173+
self,
174+
name: UIContentSizeCategory.didChangeNotification,
175+
object: nil
176+
)
177+
}
178+
179+
@objc private func handleContentSizeCategoryChange() {
180+
// Update font based on content size category
181+
let preferredFont = UIFont.preferredFont(forTextStyle: .body)
182+
font = preferredFont
183+
}
184+
}

0 commit comments

Comments
 (0)