Swift的三方库,比如 RxSwift
中的 rx
, Kingfisher
中的 kf
,给类别添加了一个前缀,就想写一个自己的前缀在项目中使用,可用区别工程中自己添加的独有扩展方法。实现起来非常简单。
新建一个swift
文件,实现命名空间的代码如下:
- import Foundation
- import UIKit
-
- /// 1.声明一个结构体, 结构体内部会持有一个任意类型的对象
- public struct ULWrapper<Base> {
- public let base: Base
- public init(_ base: Base) {
- self.base = base
- }
- }
-
- /// 2.声明协议, 让想要的类遵守此协议
- public protocol ULCompatible { }
-
- /// 3.提供协议的默认实现, object.ul, 会在ul中持有object
- extension ULCompatible {
-
- /// 实例变量及方法命名空间
- public var ul: ULWrapper<Self> {
- get { return ULWrapper(self) }
- }
-
- /// 类变量及方法命名空间
- public static var ul: ULWrapper<Self>.Type {
- get { return ULWrapper<Self>.self }
- }
-
- }
-
- /// 4.让任意对象(类,结构体,协议都可以)遵守协议
- /// 类遵守协议, 就可以使用 UIImageView.ul获取到第一步的结构体对象
- extension UIImageView : ULCompatible { }
- extension UIColor : ULCompatible { }
- /// 结构体遵守协议, 就可以使用Int.ul
- extension Int : ULCompatible { }
- /// 枚举遵守协议,就可以使用 UIView.ContentMode.ul
- extension UIView.ContentMode : ULCompatible { }
-
-
- /// 5.给指定类的对象扩展结构体的方法, 一定要加限定
- //给对象添加扩展, 加限定 where Base: 对应的子类,
- extension ULWrapper where Base: UIImageView {
-
- func setImageWithUrl(_ url : URL?,
- placeHolder : UIImage? = nil) {
- print("当前函数:",#function)
- print("self: ",self,"self.base: ",self.base)
- // self.base 就是对应的UIImageView对象
- self.base.image = placeHolder
- print("请求接口...")
- DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: DispatchWorkItem(block: {
- print("请求接口成功")
- self.base.image = UIImage.init(named: "1")
- }))
- }
-
-
- static func showInfo() {
- // 调用原有的类方法,
- // 目前只能通过这种方式, 因为当前的self是ULWrapper类型,不是实例,无法使用self.base
- UIImageView.setAnimationsEnabled(false)
- print("类方法",self, #function)
- }
-
- }
-
-
- // 演示给结构体添加扩展, 加限定 where Base == 结构体类型
- extension ULWrapper where Base == Int {
- @discardableResult
- func square() -> Int {
- // self.base 就是对应的int结构体对象
- return self.base * self.base
- }
-
- @discardableResult
- static func halfMax() -> Int {
- return Int.max / 2
- }
-
- }
-
- // 演示给枚举添加扩展,
- extension ULWrapper where Base == UIView.ContentMode {
- func printInfo() {
- // self.base 就是对应的UIView.ContentMode结构体对象
- print(self.base, "对应的Int值为", self.base.rawValue)
- }
- }
创建一个 Wrapper
的对象,它是一个容器,里面会包含一个泛型对象 base,实际上真正做事的是这个 base 对象。
写一个 Compatible
空协议,
给协议添加默认实现, 默认为它添加一个 ul
属性,这样一来,只要我想为某对象A 添加 ul 命名空间,就只需要让A
遵守 Compatible
协议: extension A: Compatible {}
,就可以让 a
拥有 a.ul
属性。
该 ul
属性返回的是一个 包裹了 a对象 的 Wrapper 对象, 在扩展结构中可以使用, self.base 获取到 a对象
假设A类拥有一个方法 doSomething()
,那么,为 Wrapper
写一个扩展方法 doSomething()
,就可以在其中调用 base.doSomething()
方法了。
- // 类使用
- let imageView = UIImageView()
- imageView.backgroundColor = .gray
- imageView.frame = CGRect.init(x: 100, y: 100, width: 170, height: 270)
- imageView.ul.setImageWithUrl(URL(string: "图片地址") ?? nil)
- self.view.addSubview(imageView)
- UIImageView.ul.showInfo()
-
- // 结构体使用
- var a = 10
- a = a.ul.square()
- print("a=",a)
- print("Int.ul.halfMax() = ",Int.ul.halfMax())
-
- // 枚举使用
- UIView.ContentMode.scaleAspectFill.ul.printInfo()
-
-
- print(type(of: self),#function,self)
- 当前函数: setImageWithUrl(_:placeHolder:)
- 请求接口...
- 类方法 ULWrapper<UIImageView> showInfo()
- a= 100
- Int.ul.halfMax() = 4611686018427387903
- UIViewContentMode 对应的Int值为 2
- ViewController viewDidLoad() <SwiftScrollViewDemo.ViewController: 0x13a507540>
- 请求接口成功
替换系统用法
例如,在 iOS 13 之后,系统默认的 present方法使用 modalPresentationStyle = .pageSheet
样式弹出,即弹出的 ViewController顶部会留部分距离是有暗色半透明背景。
但是项目中暂不需要这样的风格,所以在 Lg+UIViewController.swift
文件中,以 lg
为命名空间 对 UIViewController
进行扩展,代码如下:
- extension LgWrapper where Base: UIViewController {
- func present(_ viewController: UIViewController, presentationStyle: UIModalPresentationStyle = .fullScreen, animated: Bool, completion: (() -> Void)? = nil) {
- if viewController.modalPresentationStyle == .pageSheet {
- viewController.modalPresentationStyle = presentationStyle
- }
-
- base.present(viewController, animated: animated, completion: completion)
- }
- }
使用的时候加上 .lg.present
:
viewController.lg.present(vc2, animated: true, completion: nil)