如何唯一识别UIView并跟踪其子视图添加调用次数?兼论accessibilityIdentifier的适用性疑问
解决UIView添加/移除的计数追踪问题(避免干扰无障碍功能)
首先得说,你担心用accessibilityIdentifier来存计数会干扰系统功能,这个顾虑完全是对的!这个属性是专门给iOS的无障碍功能(比如VoiceOver、UI自动化测试)设计的,用来标识视图的语义用途,强行把它当计数器用,不仅会破坏视障用户的体验,还可能和其他依赖这个属性的代码(比如UI测试脚本)冲突,绝对不是最佳实践。
那换什么方案呢?推荐用关联对象(Associated Objects)——这是Objective-C Runtime提供的特性,Swift也能调用,专门用来给现有类(比如UIView)添加额外的关联属性,完全不会影响原类的原有功能。
下面是具体的实现步骤和代码:
第一步:导入Objective-C Runtime
在你的Swift文件顶部导入头文件:
import ObjectiveC
第二步:定义关联对象的唯一Key
我们需要一个唯一的标识来绑定给UIView添加的计数属性:
private var addCountKey: UInt8 = 0
用UInt8变量的地址作为Key,能保证在Runtime层面的唯一性,不会和其他关联属性冲突。
第三步:实现带计数逻辑的add/remove方法
修改你的foo类,用关联对象追踪每个onView的添加次数:
class foo: UIView { // 注意:原代码未初始化静态视图,这里补充初始化逻辑 static let childView: UIView = UIView() // 给UIView扩展一个关联属性,用来存添加次数 private extension UIView { var addCount: Int { get { // 从关联对象取值,默认返回0 return objc_getAssociatedObject(self, &addCountKey) as? Int ?? 0 } set { // 将新计数存入关联对象 objc_setAssociatedObject(self, &addCountKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } static func addView(onView: UIView) { let currentCount = onView.addCount if currentCount == 0 { // 只有第一次调用时,才添加子视图 onView.addSubview(childView) showHud(onView: onView) } // 计数+1 onView.addCount = currentCount + 1 } static func removeView(fromView: UIView) { let currentCount = fromView.addCount if currentCount == 1 { // 计数归0时,才移除子视图 childView.removeFromSuperview() } // 计数-1,避免出现负数 fromView.addCount = max(currentCount - 1, 0) } static func showHud(onView: UIView) { // 你的HUD显示逻辑 } }
为什么这个方案更优?
- 不干扰系统功能:关联对象是附加在UIView实例上的"隐形"属性,不会修改任何系统预设的属性,完全不会影响无障碍功能或UI自动化测试
- 逻辑更可靠:不用担心
accessibilityIdentifier被其他代码修改、或者原本就有值导致计数混乱的情况 - 符合开发规范:关联对象是iOS开发中给现有类扩展属性的标准方式,是官方推荐的解决方案
补充说明
原代码里的static let childView: UIView未初始化会导致编译错误,我已经补充了初始化逻辑,你可以根据实际需求替换成你需要的子视图类型(比如自定义HUD视图)。另外,计数逻辑中加入了max(currentCount - 1, 0)的处理,避免出现负数计数的异常情况。
内容的提问来源于stack exchange,提问作者Amais Sheikh
相关产品推荐
相关产品推荐

