官网文档:Swift - Extensions
extension可以做什么:
- Add computed instance properties and computed type properties (添加计算型实例属性和计算型类型属性)
- Define instance methods and type methods (定义实例方法和类型方法)
- Provide new initializers (提供新的构造器)
- Define subscripts (定义下标)
- Define and use new nested types (定义和使用新的嵌套类型)
- Make an existing type conform to a protocol (使已存在的类型遵守某个协议)
几个比较方便的 extension 使用技巧
-
1 .划分区域,区分代码功能
-
比如:
class AppDelegate: UIResponder { var window: UIWindow? } ///实现 App 的声明周期 extension AppDelegate: UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } func applicationWillResignActive(_ application: UIApplication) { } } extension AppDelegate { func thirdSetting() { ///配置第三方信息 } }
class ViewController: UIViewController { fileprivate (set) var tableView: UITableView! } typealias DataSource = ViewController extension DataSource: UITableViewDataSource, UITableViewDelegate { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ///code } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { ///code } }
-
-
2.像 OC 一样添加分类
OC的 Category 添加类方法和对象方法时一般会在方法前添加前缀,比如 SDWebImage 中的 UIImageView+WebCache 方法都是以 sd_开头,ReactiveObjC都是以 rac_开头,当然 swift 的 extension 也可以这样使用前缀来区分系统方法和自定义方法,不过很多大神已经不这么做了,比如 RxCocoa 里的 rx,Kinfisher 的 kf.使用如以下代码:
///Rx let textFiled = UITextField() ///textFiled的 text 有变化的时候会调用 block let dispose = textFiled.rx.text.subscribe(onNext: { (text) in print(text) }, onError: { (error) in }, onCompleted: { }, onDisposed: nil) dispose.dispose()
///Kingfisher let imgView = UIImageView() ///异步设置图片 let url = URL(string: "xxx") imgView.kf.setImage(with: url)
查看 Kinfisher 和 RxCocoa 的源码,这个 rx 和 kf 大概是以下这种形式的代码:
///这个就是 kf 和 rx 的定义 /// public struct AriSwift<Base> { public let base: Base public init(_ base: Base) {self.base = base} } public protocol AriSwiftCompatible { associatedtype CompatibleType static var `as`: AriSwift<CompatibleType>.Type { get set } var `as`: AriSwift<CompatibleType> { get set } } public extension AriSwiftCompatible { static var `as`: AriSwift<Self>.Type { get {return AriSwift<Self>.self} set {} } var `as`: AriSwift<Self> { get {return AriSwift(self)} set {} } } ///为所有继承 NSObject的类添加 as 的实例对象和类对象 extension NSObject: AriSwiftCompatible {}
///给AriSwift 添加Base是 UIScreen 的拓展 public extension AriSwift where Base: UIScreen { public static var width: CGFloat {return UIScreen.main.bounds.width} public static var height: CGFloat {return UIScreen.main.bounds.height} } ///调用 UIScreen.as.width /// 414.0
在 Swift 中如果经常会用到结构体,为结构体Base 为结构体的对象添加分类时就不能使用
Base: XX
这种形式了,以 String 为例,代码如下///记得 String 要遵守下这个协议 extension String: AriSwiftCompatible {} extension AriSwift where Base == String { ///传入时间(秒)返回字符串格式 ///``` ///e.g. 180 -> "03:00" ///``` static func formatTime(with seconds: Int) -> String { let hour = seconds / 3600 let minut = (seconds % 3600)/60 let second = seconds % 60 if hour == 0 { return String(format: "%02zd:%02zd", minut,second) }else{ return String(format: "%zd:%02zd:%02zd",hour,minut,second) } } }
使用这种方式添加extension 能很好的区分自己写的代码和系统的代码,也可以代替前缀.
还可以使用这个方法在我们的项目中封装 Objc 的第三方框架,比如 MJRefresh,MBProgressHUD
import AriSwift import MJRefresh extension AriSwift where Base: UIScrollView { func addHeader(with target: Any, action: Selector){ base.mj_header = MJRefreshNormalHeader(refreshingTarget: target, refreshingAction: action) } func addHeader(with action: @escaping (()->()) ) { base.mj_header = MJRefreshNormalHeader(refreshingBlock: { action() }) } func addFooter(with target: Any, action: Selector){ base.mj_footer = MJRefreshBackStateFooter(refreshingTarget: target, refreshingAction: action) } func addFooter(with action: @escaping (()->()) ){ base.mj_footer = MJRefreshBackStateFooter(refreshingBlock: { action() }) } func headerEndRefreshing(){ base.mj_header.endRefreshing() } func footerEndRefreshing(){ base.mj_footer.endRefreshing() } func footerEndRefreshingWithNoMoreData(){ base.mj_footer.endRefreshingWithNoMoreData() } }
import AriSwift import MBProgressHUD extension AriSwift where Base: UIViewController { func showHud(message: String, to view: UIView? = nil) -> MBProgressHUD{ let targetView = view ?? UIApplication.shared.keyWindow! let hud = MBProgressHUD.showAdded(to: targetView, animated: true) hud.label.text = message hud.removeFromSuperViewOnHide = true return hud } }
这样也可以减少项目中使用这些第三方的地方频繁的 import
p.s. 我自己制作常用的 extension 工具库 AriSwift
也可以使用 pod 'AriSwift' 安装到项目中