如何在Swift中一次性禁用所有TableView/CollectionView的滚动指示器?
一次性禁用所有UITableView/UICollectionView滚动指示器的最简方案
如果项目里有几十上百个TableView、CollectionView,不想逐个设置showsHorizontalScrollIndicator和showsVerticalScrollIndicator,也不想改Storyboard,下面两种方法可以实现一处修改全局生效:
方法1:给UIScrollView写扩展,覆盖初始化逻辑
因为UITableView和UICollectionView都是UIScrollView的子类,直接给父类做扩展,所有子类都会自动继承这个设置:
extension UIScrollView { override open func awakeFromNib() { super.awakeFromNib() hideScrollIndicators() } override public init(frame: CGRect) { super.init(frame: frame) hideScrollIndicators() } required public init?(coder: NSCoder) { super.init(coder: coder) hideScrollIndicators() } private func hideScrollIndicators() { showsHorizontalScrollIndicator = false showsVerticalScrollIndicator = false } }
不管是代码里创建的实例,还是从Storyboard/XIB加载的,初始化时都会自动执行hideScrollIndicators(),完全不用修改原有代码。
方法2:用Runtime方法交换(Method Swizzling)
如果不想重写初始化方法,可以用Swift的Runtime特性替换系统的初始化方法,注入隐藏指示器的逻辑:
import UIKit import ObjectiveC extension UIScrollView { static let swizzleScrollIndicators: Void = { // 交换frame初始化方法 let originalFrameInit = #selector(UIScrollView.init(frame:)) let swizzledFrameInit = #selector(UIScrollView.swizzled_frameInit(frame:)) guard let originalMethod = class_getInstanceMethod(Self.self, originalFrameInit), let swizzledMethod = class_getInstanceMethod(Self.self, swizzledFrameInit) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) // 交换coder初始化方法(处理XIB/Storyboard) let originalCoderInit = #selector(UIScrollView.init(coder:)) let swizzledCoderInit = #selector(UIScrollView.swizzled_coderInit(coder:)) guard let originalCoderMethod = class_getInstanceMethod(Self.self, originalCoderInit), let swizzledCoderMethod = class_getInstanceMethod(Self.self, swizzledCoderInit) else { return } method_exchangeImplementations(originalCoderMethod, swizzledCoderMethod) }() @objc private func swizzled_frameInit(frame: CGRect) { // 先调用原方法完成初始化 self.swizzled_frameInit(frame: frame) hideScrollIndicators() } @objc private func swizzled_coderInit(coder: NSCoder) { guard self.swizzled_coderInit(coder: coder) != nil else { return } hideScrollIndicators() } private func hideScrollIndicators() { showsHorizontalScrollIndicator = false showsVerticalScrollIndicator = false } } // 在App启动时触发方法交换(只执行一次) extension UIApplication { override open var next: UIResponder? { UIScrollView.swizzleScrollIndicators return super.next } }
关于你问的几个问题
- Swift里的钩子函数:没有像JS那样的原型钩子,但可以通过重写
awakeFromNib、初始化方法,或者用Runtime方法交换来实现“实例化时自动执行代码”的效果。 - 类似JS原型修改的方案:上面的方法交换就相当于修改了UIScrollView的“原型”逻辑,所有实例都会生效。性能损耗几乎可以忽略——只有第一次调用交换后的方法时会有极微小的开销,后续执行和原生方法效率完全一致。而方法1的扩展重写方式则完全没有额外性能损耗。
内容的提问来源于stack exchange,提问作者tanmoy
相关产品推荐
相关产品推荐

