Swift:UIKit 扩展工具箱

原创:问题解决型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 1、UIApplication 的扩展
  • 2、UIDevice 的扩展
  • 3、UIColor 的扩展
  • 4、UIFont 的扩展
  • 5、UIImage 的扩展
  • 6、UIImageView 的扩展
  • 7、UIResponder 的扩展
  • 8、UIScreen 的扩展
  • 9、UIView 的扩展
  • 10、CALayer 的扩展
  • 11、UIWindow 的扩展

1、UIApplication 的扩展

extension UIApplication
documentsURL:documents的url
var documentsURL: URL? {
    get {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
    }
}
documentsPath:documents的路径
var documentsPath: String? {
    get {
        return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
    }
}
cachesURL:caches的url
var cachesURL: URL? {
    get {
        return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).last
    }
}
cachesPath:caches的路径
var cachesPath: String? {
    get {
        return NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first
    }
}
libraryURL:library的url
var libraryURL: URL? {
    get {
        return FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).last
    }
}
libraryPath:library的路径
var libraryPath: String? {
    get {
        return NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first
    }
}
appBundleName:app名称
var appBundleName: String? {
    get {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
    }
}
appBundleID:app唯一识别码
var appBundleID: String? {
    get {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleIdentifier") as? String
    }
}
appVersion:app发布版本
var appVersion: String? {
    get {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
    }
}
appBuildVersion:app 的 build 版本
var appBuildVersion: String? {
    get {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String
    }
}
memoryUsage:内存使用情况
var memoryUsage: Float? {
    get {
        var info = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout.size(ofValue: info) / MemoryLayout.size)
        let kerr = withUnsafeMutablePointer(to: &info) { infoPtr in
            return infoPtr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { (machPtr: UnsafeMutablePointer) in
                return task_info(
                    mach_task_self_,
                    task_flavor_t(MACH_TASK_BASIC_INFO),
                    machPtr,
                    &count
                )
            }
        }
        guard kerr == KERN_SUCCESS else {
            return nil
        }
        return Float(info.resident_size) / (1024 * 1024)
    }
}

2、UIDevice 的扩展

public extension UIDevice
systemName、systemVersion、model、uuid:获取系统名称和版本
let uuid = UIDevice.current.identifierForVendor?.uuidString ?? ""
let model = UIDevice.current.model
let systemName = UIDevice.current.systemName
let systemVersion = UIDevice.current.systemVersion
stringWithUUID:获取UUID
let strUUID = UIDevice.stringWithUUID
static var stringWithUUID: String {
    get {
        let uuid = CFUUIDCreate(nil)
        let string = CFUUIDCreateString(nil, uuid)
        return String(describing: string)
    }
}
isSimulator:是否模拟器
UIDevice.current.isSimulator
var isSimulator: Bool {
    get {
        var isSim = false
        #if arch(i386) || arch(x86_64)
            isSim = true
        #endif
        return isSim
    }
}
isJailbroken:检查设备是否越狱
UIDevice.current.isJailbroken
var isJailbroken: Bool {
    get {
        if isSimulator {
            return false
        }
        
        let paths = [
            "/Applications/Cydia.app",
            "/private/var/lib/apt/",
            "/private/var/lib/cydia",
            "/private/var/stash"
        ]
        
        for path in paths {
            if FileManager.default.fileExists(atPath: path) {
                return true
            }
        }
        
        let path = "/private/\(UIDevice.stringWithUUID)"
        do {
            try "test".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
            try FileManager.default.removeItem(atPath: path)
            return true
        }
        catch {
            return false
        }
    }
}
deviceMachine:机型
var deviceMachine: String {
    get {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, (value != 0)
            else {
                return identifier
            }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }
        return identifier
    }
}
deviceName:根据机型匹配设备名称
UIDevice.current.deviceName
var deviceName: String {
    get {
        let deviceMachine = self.deviceMachine
        switch deviceMachine {
            case"iPod5,1":
                return"iPod Touch 5"
            case"iPod7,1":
                return"iPod Touch 6"
            case"iPhone3,1", "iPhone3,2", "iPhone3,3":
                return"iPhone4"
            case"iPhone4,1":
                return"iPhone4s"
            case"iPhone5,1","iPhone5,2":
                return"iPhone5"
            case"iPhone5,3", "iPhone5,4":
                return"iPhone5c"
            case"iPhone6,1", "iPhone6,2":
                return"iPhone5s"
            case"iPhone7,2":
                return"iPhone6"
            case"iPhone7,1":
                return"iPhone6 Plus"
            case"iPhone8,1":
                return"iPhone6s"
            case"iPhone8,2":
                return"iPhone6s Plus"
            case"iPhone8,4":
                return"iPhoneSE 1"
            case"iPhone9,1", "iPhone9,3":
                return"iPhone7"
            case"iPhone9,2", "iPhone9,4":
                return"iPhone7 Plus"
            case"iPhone10,1", "iPhone10,4":
                return"iPhone8"
            case"iPhone10,5", "iPhone10,2":
                return"iPhone8 Plus"
            case"iPhone10,3", "iPhone10,6":
                return"iPhoneX"
            case"iPhone11,2":
                return"iPhoneXS"
            case"iPhone11,6", "iPhone11,4":
                return"iPhoneXS MAX"
            case"iPhone11,8":
                return"iPhoneXR"
            case"iPhone12,1":
                return"iPhone11"
            case"iPhone12,3":
                return"iPhone11 Pro"
            case"iPhone12,5":
                return"iPhone11 Pro Max"
            case"iPhone12,8":
                return"iPhoneSE 2"
            case"iPhone13,1":
                return"iPhone12 mini"
            case"iPhone13,2":
                return"iPhone12"
            case"iPhone13,3":
                return"iPhone12 Pro"
            case"iPhone13,4":
                return"iPhone12 Pro MAX"
            
            case"iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":
                return"iPad 2"
            case"iPad3,1", "iPad3,2", "iPad3,3":
                return"iPad 3"
            case"iPad3,4", "iPad3,5", "iPad3,6":
                return"iPad 4"
            case"iPad6,11", "iPad6,12":
                return"iPad 5"
            case"iPad7,5", "iPad7,6":
                return"iPad 6"
            case"iPad7,11", "iPad7,12":
                return"iPad 7"
            case"iPad11,6", "iPad11,7":
                return"iPad 8"
                
            case"iPad4,1", "iPad4,2", "iPad4,3":
                return"iPad Air"
            case"iPad5,3","iPad5,4":
                return"iPad Air 2"
            case"iPad11,3","iPad11,4":
                return"iPad Air 3"
            case"iPad13,1","iPad13,2":
                return"iPad Air 4"
                
            case"iPad2,5", "iPad2,6", "iPad2,7":
                return"iPad Mini"
            case"iPad4,4", "iPad4,5", "iPad4,6":
                return"iPad Mini 2"
            case"iPad4,7", "iPad4,8", "iPad4,9":
                return"iPad Mini 3"
            case"iPad5,1","iPad5,2":
                return"iPad Mini 4"
            case"iPad11,1","iPad12,2":
                return"iPad Mini 5"
                
            case"iPad6,7","iPad6,8","iPad6,3","iPad6,4","iPad7,3","iPad7,4"
            ,"iPad8,1","iPad8,2","iPad8,3","iPad8,4":
                return"iPad Pro"
            case"iPad7,1","iPad7,2","iPad8,9","iPad8,10":
                return"iPad Pro 2"
            case"iPad8,5","iPad8,6","iPad8,7","iPad8,8",
                "iPad12,4","iPad13,5","iPad13,6","iPad13,7":
                return"iPad Pro 3"
            case"iPad8,11","iPad8,12":
                return"iPad Pro 4"
            case"iPad13,8","iPad13,9","iPad13,10","iPad13,11":
                return"iPad Pro 4"

            case"AppleTV5,3":
                return"Apple TV"
            case"i386","x86_64":
                return"Simulator"
            default:
                return deviceMachine
          }
    }
}

3、UIColor 的扩展

public extension UIColor 
hex(_ hex: Int):使用16进制数字来设置颜色
UIColor.hex(0x8f8f98)
/**
 使用16进制数生成UIColor,默认Alpha为1.0
 - parameter hex: 16进制数,例如:0xffffff
 - returns: UIColor
 */
class func hex(_ hex: Int) -> UIColor {
    return UIColor(
        red: ((CGFloat)((hex & 0xFF0000) >> 16)) / 255.0,
        green: ((CGFloat)((hex & 0xFF00) >> 8)) / 255.0,
        blue: (CGFloat(hex & 0xFF)) / 255.0,
        alpha: 1.0)
}
hex(_ hex: Int, alpha: CGFloat):使用16进制数字和透明度来设置颜色
/**
 使用16进制数生成UIColor,默认Alpha为1.0
 - parameter hex:   16进制数,例如:0xffffff
 - parameter alpha: alpha值
 - returns: UIColor
 */
class func hex(_ hex: Int, alpha: CGFloat) -> UIColor {
    return UIColor(
        red: ((CGFloat)((hex & 0xFF0000) >> 16)) / 255.0,
        green: ((CGFloat)((hex & 0xFF00) >> 8)) / 255.0,
        blue: (CGFloat(hex & 0xFF)) / 255.0,
        alpha: alpha)
}
hexString(_ hexString: String):使用16进制字符串来设置颜色
UIColor(hexString:"#846BFF")
class func hexString(_ hexString: String) -> UIColor {
    var hex = hexString
    if hex.hasPrefix("#") {
        hex.remove(at: hexString.startIndex)
    }
    if hex.count != 6 {
        return UIColor.black
    }
    // 存储转换后的数值
    var red:UInt32 = 0, green:UInt32 = 0, blue:UInt32 = 0
    
    // 分别进行转换
    Scanner(string: hex[0..<2]).scanHexInt32(&red)
    Scanner(string: hex[2..<4]).scanHexInt32(&green)
    Scanner(string: hex[4..<6]).scanHexInt32(&blue)
    
    return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: 1.0)
}
blendWithAlpha(_ alpha: CGFloat):在现有的UIColor基础上混入alpha,如原有颜色已经设置alpha通道,那么取旧值与传入的alpha的积作为新颜色的alpha值
var highlightedColor = normalColor.blendWithAlpha(0.7)
/// - Parameter alpha: 需要混入的alpha
/// - Returns: 混入新alpha值后的颜色
func blendWithAlpha(_ alpha: CGFloat) -> UIColor {
    var r: CGFloat = 0.0
    var g: CGFloat = 0.0
    var b: CGFloat = 0.0
    var a: CGFloat = 0.0
    
    getRed(&r, green: &g, blue: &b, alpha: &a)
    return UIColor(red: r, green: g, blue: b, alpha: a * alpha)
}
==(lhs: UIColor, rhs: UIColor):判断两个颜色是否相同
static func ==(lhs: UIColor, rhs: UIColor) -> Bool {
    let tolerance: CGFloat = 0.01
    
    var r1: CGFloat = 0.0
    var g1: CGFloat = 0.0
    var b1: CGFloat = 0.0
    var a1: CGFloat = 0.0
    
    var r2: CGFloat = 0.0
    var g2: CGFloat = 0.0
    var b2: CGFloat = 0.0
    var a2: CGFloat = 0.0
    
    lhs.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
    rhs.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
    
    return abs(r1-r2) <= tolerance && abs(g1-g2) <= tolerance && abs(b1-b2) <= tolerance && abs(a1-a2) <= tolerance
}
!=(lhs: UIColor, rhs: UIColor):判断两个颜色是否不同
static func !=(lhs: UIColor, rhs: UIColor) -> Bool {
    return !(lhs == rhs)
}
init(r: CGFloat, g: CGFloat, b: CGFloat, _ alpha: CGFloat = 1.0):使用rgba来设置颜色
convenience init(r: CGFloat, g: CGFloat, b: CGFloat, _ alpha: CGFloat = 1.0) {
    self.init(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: alpha)
}
init(_ hexString:String, _ alpha: CGFloat = 1.0):使用16进制字符串和透明度设置颜色
convenience init(_ hexString:String, _ alpha: CGFloat = 1.0) {
    let scanner:Scanner = Scanner(string:hexString)
    var valueRGB:UInt32 = 0
    if scanner.scanHexInt32(&valueRGB) == false {
        self.init(red: 0,green: 0,blue: 0,alpha: 0)
    } else {
        self.init(
            red:CGFloat((valueRGB & 0xFF0000)>>16)/255.0,
            green:CGFloat((valueRGB & 0x00FF00)>>8)/255.0,
            blue:CGFloat(valueRGB & 0x0000FF)/255.0,
            alpha:CGFloat(alpha)
        )
    }
}

4、UIFont 的扩展

public extension UIFont
目前需求仅苹果PingFang字体
public enum FontType:String {
    case Regular    = "PingFangSC-Regular"
    case Medium     = "PingFangSC-Medium"
    case Semibold   = "PingFangSC-Semibold"
    case Light      = "PingFangSC-Light"
    case Ultralight = "PingFangSC-Ultralight"
    case Thin       = "PingFangSC-Thin"
}
font(size: CGFloat):设置系统字体大小
class func font(size: CGFloat) -> UIFont {
    return UIFont.systemFont(ofSize: size)
}
font(withName name:FontType, withSize size:CGFloat):设置PingFang字体名称和大小
class func font(withName name:MLFontType, withSize size:CGFloat) -> UIFont {
    return UIFont.init(name: name.rawValue, size: size) ?? UIFont.systemFont(ofSize: size)
}
font(name: String, size: CGFloat):设置字体名称和大小
class func font(name: String, size: CGFloat) -> UIFont {
    if let font = UIFont(name: name, size: size) {
        return font
    } else {
        return UIFont.systemFont(ofSize: size)
    }
}

5、UIImage 的扩展

import ImageIO

extension UIImage
gif(data: Data):根据图片数据生成GIF动图
public class func gif(data: Data) -> UIImage? {
    guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
        print("SwiftGif: Source for the image does not exist")
        return nil
    }

    return UIImage.animatedImageWithSource(source)
}
gif(url: String):根据图片url生成GIF动图
public class func gif(url: String) -> UIImage? {
    // 验证 URL
    guard let bundleURL = URL(string: url) else {
        print("SwiftGif: This image named \"\(url)\" does not exist")
        return nil
    }

    // 验证 data
    guard let imageData = try? Data(contentsOf: bundleURL) else {
        print("SwiftGif: Cannot turn image named \"\(url)\" into NSData")
        return nil
    }

    return gif(data: imageData)
}
gif(name: String):根据图片名称生成GIF动图
public class func gif(name: String) -> UIImage? {
    // 检查gif文件是否存在
    guard let bundleURL = Bundle.main
      .url(forResource: name, withExtension: "gif") else {
        print("SwiftGif: This image named \"\(name)\" does not exist")
        return nil
    }

    // 验证data
    guard let imageData = try? Data(contentsOf: bundleURL) else {
        print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
        return nil
    }

    return gif(data: imageData)
}
gif(asset: String):根据资源生成GIF动图
public class func gif(asset: String) -> UIImage? {
    // 使用资源目录创建数据
    guard let dataAsset = NSDataAsset(name: asset) else {
        print("SwiftGif: Cannot turn image named \"\(asset)\" into NSDataAsset")
        return nil
    }

    return gif(data: dataAsset.data)
}
animatedImageWithSource(_ imgSource: CGImageSource):生成GIF动图的底层方法
internal class func animatedImageWithSource(_ imgSource: CGImageSource) -> UIImage? {
    let imageCount = CGImageSourceGetCount(imgSource)
    var images = [UIImage]()
    var totalDuration : TimeInterval = 0
    
    for i in 0...imageCount {
        guard let cgImage = CGImageSourceCreateImageAtIndex(imgSource, i, nil) else {
            continue
        }
        guard let properties : NSDictionary = CGImageSourceCopyPropertiesAtIndex(imgSource, i, nil) else {
            continue
        }
        guard let gifDic = properties[kCGImagePropertyGIFDictionary] as? NSDictionary else {
            continue
        }
        guard let duration = gifDic[kCGImagePropertyGIFDelayTime] as? NSNumber else {
            continue
        }
        totalDuration += duration.doubleValue
        let image = UIImage(cgImage: cgImage)
        images.append(image)
    }
    let animation = UIImage.animatedImage(with: images,
        duration: Double(totalDuration))
    return animation
}
fromColor(_ color: UIColor):获取颜色填充的图片
static func fromColor(_ color: UIColor) -> UIImage {
    let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()
    context!.setFillColor(color.cgColor)
    context!.fill(rect)
    let img = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return img!
}
grayImage():将彩色图片置灰来获取灰度图片
func grayImage() -> UIImage? {
   UIGraphicsBeginImageContext(self.size)
   let colorSpace = CGColorSpaceCreateDeviceGray()
   let context = CGContext(data: nil , width: Int(self.size.width), height: Int(self.size.height),bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)
   context?.draw(self.cgImage!, in: CGRect.init(x: 0, y: 0, width: self.size.width, height: self.size.height))
   let cgImage = context!.makeImage()
   let grayImage = UIImage(cgImage: cgImage!, scale: self.scale, orientation: self.imageOrientation)
   return grayImage
}
getSubImage(_ rect: CGRect):截取图片上的指定区域
/// - Parameter rect: 截取区域的frame,注:这里的尺寸是相对于原图片尺寸的
/// - Returns: 生成的新图片
func getSubImage(_ rect: CGRect) -> UIImage? {
    let imageRef = cgImage!.cropping(to: rect)
    let width = imageRef?.width ?? 0
    let height = imageRef?.height ?? 0
    let smallBounds = CGRect(x: 0, y: 0, width: width, height: height)
    UIGraphicsBeginImageContext(smallBounds.size)

    if let image = imageRef {
        let context = UIGraphicsGetCurrentContext()
        context?.draw(image, in: smallBounds)
        let smallImage = UIImage(cgImage: image)
        UIGraphicsEndImageContext()
        return smallImage
    } else {
        return nil
    }
}
scaleToSize(_ size: CGSize,type:scaleType = .aspectToFit):图片压缩
enum scaleType {
    case fill               //  根据size进行等比例缩放,周围填充白色背景
    case aspectToFill       //  根据size图片拉伸填充
    case aspectToFit        //  根据size进行等比例缩放
}
/// - Parameters:
///   - size: 目标尺寸
///   - type: 压缩方式
/// - Returns: 压缩后的图片
func scaleToSize(_ size: CGSize,type:scaleType = .aspectToFit) -> UIImage? {
    var width = CGFloat(cgImage?.width ?? 0)
    var height = CGFloat(cgImage?.height ?? 0)
    
    if width.isEqual(to: 0) || height.isEqual(to: 0) {
        return self
    }
    
    let verticalRadio = size.height/height
    let horizontalRadio = size.width/width
    var radio:CGFloat = 1
    if verticalRadio > 1 && horizontalRadio > 1 {
        radio = verticalRadio > horizontalRadio ? horizontalRadio : verticalRadio
    } else {
        radio = verticalRadio < horizontalRadio ? verticalRadio : horizontalRadio
    }

    width = width*radio
    height = height*radio

    if type == .fill {
        let xPos = (size.width - width)/2
        let yPos = (size.height - height)/2
        UIGraphicsBeginImageContext(size)
        draw(in: CGRect(x: xPos, y: yPos, width: width, height: height))
    } else if(type ==  .aspectToFit){
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        draw(in: CGRect(x: 0, y: 0, width: width, height: height))
    } else if(type == .aspectToFill) {
        UIGraphicsBeginImageContext(size)
        draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    }
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}
compressImage(_ maxLength: Int):压缩上传图片到指定字节,二分法压缩效率高于传统while循环方式,一张3K图片,耗时仅为1/5,图片越大,差别越明显
/**
 *  maxLength 压缩后最大字节大小,根据图片情况可能会存在没有压缩到指定大小的情况
 *  return 压缩后图片的二进制数据
 */
func compressImage(_ maxLength: Int) -> Data? {
    var compression: CGFloat = 1
    guard var data = UIImageJPEGRepresentation(self,1) else { return nil }
    if data.count < maxLength {
        return data
    }
    print("压缩前kb", data.count / 1024, "KB")
    var max: CGFloat = 1
    var min: CGFloat = 0
    for _ in 0..<6 {
        compression = (max + min) / 2
        data = UIImageJPEGRepresentation(self,compression)!
        if CGFloat(data.count) < CGFloat(maxLength) * 0.9 {
            min = compression
        } else if data.count > maxLength {
            max = compression
        } else {
            break
        }
    }
    print("压缩后kb", data.count / 1024, "KB")
    return data
}
combine(subImage:UIImage, anchor:CGPoint):图片合成
/// - Parameters:
///   - subImage: 子图片,需要提前处理到合适的尺寸
///   - anchor: 锚点,子图片在父图片的中心点
/// - Returns: 合成后的图片
func combine(subImage:UIImage, anchor:CGPoint) -> UIImage? {
    let width = self.size.width
    let height = self.size.height
    
    UIGraphicsBeginImageContext(CGSize(width: width, height: height))
    self.draw(at: CGPoint(x: 0, y: 0))
    
    let x = anchor.x - subImage.size.width / 2
    let y = anchor.y - subImage.size.height / 2
    subImage.draw(at: CGPoint(x: x, y: y))
    
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
Pod库获取资源图片
public static func getImageFromBundle(imageName:String) -> UIImage {
    var image:UIImage = UIImage()
    if let bundleURL = Bundle.main.url(forResource: "MLBaseUIKit", withExtension: "bundle") {
        let bundle = Bundle(url: bundleURL)
        let scale = UIScreen.main.scale
        var scaleName = ""
        if scale <= 2 {
            scaleName = "\(imageName)@2x.png"
        } else {
            scaleName = "\(imageName)@3x.png"
        }
        if let path = (bundle?.path(forResource: scaleName, ofType: nil)) {
            image = UIImage(contentsOfFile: path) ??  UIImage()
        }
    }
    return image
}

6、UIImageView 的扩展

import Kingfisher

public extension UIImageView
cancelImageRequest():取消当前UIImageView的图片下载任务
func cancelImageRequest() {
     self.kf.cancelDownloadTask()
}
setImageWithRoundCorner(urlString: String...):下载字符串来下载带有圆角的网络图片
avatarImageview.setImageWithRoundCorner(urlString: avatar, placeholder: image)
///- urlString: 图片的url string
///- placeholder: 占位图
///- cornerRadius: 图片圆角大小,,默认为UIImageView的bounds.size小边的二分之一
///- size: 图片的size,默认为UIImageView的bounds.size
///- transform: 是否需要转化成七牛URL(包含七牛所需参数)
///- progressBlock: 进度回调block
///- completionHandler: 下载完成block
func setImageWithRoundCorner(
    urlString: String,
    placeholder: UIImage?,
    cornerRadius: CGFloat? = nil,
    size: CGSize? = nil,
    qiniuURLTransform transform: Bool = true,
    progressBlock: DownloadProgressBlock? = nil,
    completionHandler: CompletionHandler? = nil) {
    
    let s = size ?? bounds.size
    let radius = cornerRadius ?? min(s.width, s.height) / 2
    let url = transform ? urlString.mlURL(width: s.width, height: s.height) : URL(string: urlString)
    setImageWithRoundCorner(url: url, placeholder: placeholder, cornerRadius: radius, size: s, qiniuURLTransform: false, progressBlock: progressBlock, completionHandler: completionHandler)
}
setImageWithRoundCorner(url: URL?...):使用url来下载带有圆角的网络图片
///- url: 图片的url string
func setImageWithRoundCorner(
    url: URL?,
    placeholder: UIImage?,
    cornerRadius: CGFloat? = nil,
    size: CGSize? = nil,
    qiniuURLTransform transform: Bool = true,
    progressBlock: DownloadProgressBlock? = nil,
    completionHandler: CompletionHandler? = nil) {
    
    let s = size ?? bounds.size
    guard s.width != 0 && s.height != 0 else {
        return
    }
    
    let radius = cornerRadius ?? min(s.width, s.height) / 2
    let scale = UIScreen.main.scale
    let roundProcessor = RoundCornerImageProcessor(cornerRadius: radius * scale, targetSize: CGSize(width: s.width * scale, height: s.height * scale))
    let targetURL = transform ? url?.absoluteString.mlURL(width: s.width, height: s.height) : url
    
    setImage(url: targetURL, placeholder: placeholder, qiniuURLTransform: false, options: [.processor(roundProcessor), .cacheSerializer(RoundCornerImageCacheSerializer.default)], progressBlock: progressBlock, completionHandler: completionHandler)
}
public struct RoundCornerImageCacheSerializer: CacheSerializer {
    static let `default` = RoundCornerImageCacheSerializer()
    private init() {}
    
    public func data(with image: Image, original: Data?) -> Data? {
        return image.kf.pngRepresentation()
    }
    
    public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
        let options = options ?? []
        let image = Image(data: data, scale: options.scaleFactor)
        return image
    }
}
setImage(urlString: String...):使用urlString下载网络图片
cell.imgView.setImage(urlString: item.imgName, placeholder:UIImage(named: "placeholder_small"))
///- options: 下载器选项
func setImage(
    urlString: String?,
    placeholder: UIImage?,
    size: CGSize? = nil,
    qiniuURLTransform transform: Bool = true,
    options: KingfisherOptionsInfo? = nil,
    progressBlock: DownloadProgressBlock? = nil,
    completionHandler: CompletionHandler? = nil) {
    
    let s = size ?? CGSize(width: min(bounds.size.width, 1242), height: min(bounds.size.height, 2208))
    let url = transform ? urlString?.decodeUrl().addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)?.mlURL(width: s.width, height: s.height) : URL(string: urlString?.decodeUrl().addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")
    
    setImage(url: url, placeholder: placeholder, qiniuURLTransform: false, options: options, progressBlock: progressBlock, completionHandler: completionHandler)
}
setImage(url: URL?):使用url下载网络图片
func setImage(
    url: URL?,
    placeholder: UIImage?,
    size: CGSize? = nil,
    qiniuURLTransform transform: Bool = true,
    options: KingfisherOptionsInfo? = nil,
    progressBlock: DownloadProgressBlock? = nil,
    completionHandler: CompletionHandler? = nil) {
    
    let s = size ?? CGSize(width: min(bounds.size.width, 1242), height: min(bounds.size.height, 2208))
    var targetURL :URL!
    let largetImage = ((s.height / s.width) > 3) || ((s.height / s.width) < 0.333)
    if size==nil,largetImage {
        targetURL = url
    } else {
        targetURL = transform ? url?.absoluteString.decodeUrl().addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)?.mlURL(width: s.width, height: s.height) : url
    }
    kf.setImage(with: targetURL, placeholder: placeholder, options: options, progressBlock: progressBlock, completionHandler: completionHandler)
}
loadGif(name: String):通过string来加载gif图片
public func loadGif(name: String) {
    DispatchQueue.global().async {
        let image = UIImage.gif(name: name)
        DispatchQueue.main.async {
            self.image = image
        }
    }
}
loadGif(asset: String):通过asset来加载gif图片
public func loadGif(asset: String) {
    DispatchQueue.global().async {
        let image = UIImage.gif(asset: asset)
        DispatchQueue.main.async {
            self.image = image
        }
    }
}

7、UIResponder 的扩展

extension UIResponder
currentFirstResponder ():获取当前Window下的First Responder
UIResponder.currentFirstResponder()?.resignFirstResponder()
private weak var global_currentFirstResponder: UIResponder?
open class func currentFirstResponder () -> UIResponder? {
    global_currentFirstResponder = nil
    UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
    return global_currentFirstResponder
}
@objc func findFirstResponder (sender: AnyObject?) {
    global_currentFirstResponder = self
}

8、UIScreen 的扩展

public extension UIScreen
size:屏幕尺寸
static var size: CGSize {
    get {
        return self.main.bounds.size
    }
}
width:屏幕宽度
static var width: CGFloat {
    get {
        return self.main.bounds.size.width
    }
}
width:屏幕高度
static var height: CGFloat {
    get {
        return self.main.bounds.size.height
    }
}
isiPhoneXAndHigher:是否是iPhoneX以上系列
static var isiPhoneXAndHigher:Bool {
    get {
        let edgeInset = safeAreaEdgeInsets
        if edgeInset.bottom.isEqual(to: 34) || edgeInset.bottom.isEqual(to: 21) {
            return true
        }
        return false
    }
}
statusBarHeight:状态栏高度
static var statusBarHeight: CGFloat {
    get {
        return safeAreaEdgeInsets.top
    }
}
navigationBarHeight:导航栏高度
static var navigationBarHeight: CGFloat {
    get {
        return 44
    }
}
tabBarHeight:Tab栏高度
static var tabBarHeight: CGFloat {
    get {
        return 49
    }
}
topBarHeight:顶部状态栏+导航栏高度
static var topBarHeight: CGFloat {
    get {
        return statusBarHeight + navigationBarHeight
    }
}
safeAreaEdgeInsets:安全区域
static var safeAreaEdgeInsets: UIEdgeInsets {
    get {
        guard #available(iOS 11.0, *), let safeAreaInsets = UIApplication.shared.delegate?.window??.safeAreaInsets else {
            return UIEdgeInsets()
        }
        return safeAreaInsets
    }
}

9、UIView 的扩展

public extension UIView
clipRectCorner(direction: UIRectCorner, cornerRadius: CGFloat) :裁剪 view 的圆角
func clipRectCorner(direction: UIRectCorner, cornerRadius: CGFloat) {
    let cornerSize = CGSize(width: cornerRadius, height: cornerRadius)
    let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: direction, cornerRadii: cornerSize)
    let maskLayer = CAShapeLayer()
    maskLayer.frame = bounds
    maskLayer.path = maskPath.cgPath
    layer.addSublayer(maskLayer)
    layer.mask = maskLayer
}
addSelectedTap(handler: ((_ view:UIView?)->())?) :添加点击手势
left_back_button.addSelectedTap { [weak self] (button) in
func addSelectedTap(handler: ((_ view:UIView?)->())?) {
    self.isUserInteractionEnabled = true
    let tap = UITapGestureRecognizer(target: self) { (tap) in
        handler?(tap.view)
    }
    self.addGestureRecognizer(tap)
}
public extension UITapGestureRecognizer {
    public convenience init(target: Any?, handler: ((_ tap:UITapGestureRecognizer)->Void)?) {
        self.init(target: nil, action: nil)
        self.addTarget(self, action: #selector(didTapedBarButton))
        self.boost_handler = handler
    }
    
    @objc private func didTapedBarButton() {
        self.boost_handler?(self)
    }
    
    class TapWrapper {
        var closure: ((_ tap:UITapGestureRecognizer) -> Void)?
        init(_ closure: ((_ tap:UITapGestureRecognizer) -> Void)?) {
            self.closure = closure
        }
    }
    
    fileprivate struct AssociatedKeys {
        static var HandlerKey = "HandlerKeyTap"
    }
    
    private  var boost_handler: ((_ tap:UITapGestureRecognizer)->Void)? {
        get {
            let obj = objc_getAssociatedObject(self, &AssociatedKeys.HandlerKey) as? TapWrapper
            return obj?.closure
        }
        
        set(newHandler) {
            objc_setAssociatedObject(self, &AssociatedKeys.HandlerKey, TapWrapper(newHandler), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}
left:视图的左边
var left: CGFloat {
    get {
        return frame.origin.x
    }
    set {
        var rect = frame
        rect.origin.x = newValue
        frame = rect
    }
}
right:视图的右边
var right: CGFloat {
    get {
        return frame.origin.x + frame.size.width
    }
    set {
        var rect = frame
        rect.origin.x = newValue - rect.size.width
        frame = rect
    }
}
top:视图的上边
var top: CGFloat {
    get {
        return frame.origin.y
    }
    set {
        var rect = frame
        rect.origin.y = newValue
        frame = rect
    }
}
bottom:视图的下边
var bottom: CGFloat {
    get {
        return frame.origin.y + frame.size.height
    }
    set {
        var rect = frame
        rect.origin.y = newValue - frame.size.height
        frame = rect
    }
}
width:视图的宽度
var width: CGFloat {
    get {
        return frame.size.width
    }
    set {
        var rect = frame
        rect.size.width = newValue
        frame = rect
    }
}
height:视图的高度
var height: CGFloat {
    get {
        return frame.size.height
    }
    set {
        var rect = frame
        rect.size.height = newValue
        frame = rect
    }
}
centerX:视图x轴中心位置
var centerX: CGFloat {
    get {
        return center.x
    }
    set {
        center = CGPoint(x: newValue, y: center.y)
    }
}
centerY:视图y轴中心位置
var centerY: CGFloat {
    get {
        return center.y
    }
    set {
        center = CGPoint(x: center.x, y: newValue)
    }
}
origin:视图的起始点
var origin: CGPoint {
    get {
        return frame.origin
    }
    set {
        var rect = frame
        rect.origin = newValue
        frame = rect
    }
}
size:视图的尺寸
var size: CGSize {
    get {
        return frame.size
    }
    set {
        var rect = frame
        rect.size = newValue
        frame = rect
    }
}
viewController:寻找视图所在的控制器
var viewController: UIViewController? {
    get {
        var aView: UIView? = self
        while aView != nil {
            aView = aView?.superview
            let nextResponder = aView?.next
            if nextResponder?.isKind(of: UIViewController.self) == true {
                return nextResponder as? UIViewController
            }
        }
        return nil
    }
}
snapshotImage():快照
func snapshotImage() -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
    guard let context = UIGraphicsGetCurrentContext() else { return nil }
    layer.render(in: context)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}
setLayerShadow(color: UIColor, offset: CGSize, radius: CGFloat):设置图层阴影
func setLayerShadow(color: UIColor, offset: CGSize, radius: CGFloat) {
    layer.shadowColor = color.cgColor
    layer.shadowOffset = offset
    layer.shadowRadius = radius
    layer.shadowOpacity = 1
    layer.shouldRasterize = true
    layer.rasterizationScale = UIScreen.main.scale
}
removeAllSubviews():移除所有图层
func removeAllSubviews() {
    while subviews.count > 0 {
        subviews.last?.removeFromSuperview()
    }
}
setViewtoRound():让视图变成圆形
func setViewtoRound() {
    self.setRedius(self.height/2.0)
}
setRedius(_ radius: CGFloat) :设置视图圆角
func setRedius(_  radius: CGFloat) {
    self.layer.masksToBounds = true
    self.layer.cornerRadius = radius
}
setViewCorner(radius:CGFloat,corner:UIRectCorner):设置视图某几个位置的圆角
func setViewCorner(radius:CGFloat,corner:UIRectCorner){
    if #available(iOS 11.0, *) {
        self.layer.cornerRadius = radius
        self.layer.maskedCorners = CACornerMask(rawValue: corner.rawValue)
    } else {
        let fieldPath = UIBezierPath.init(roundedRect: self.bounds, byRoundingCorners: corner, cornerRadii:CGSize(width: radius, height: radius))
        let fieldLayer = CAShapeLayer()
        fieldLayer.frame = self.bounds
        fieldLayer.path = fieldPath.cgPath
        self.layer.mask = fieldLayer
    }
}
showRedPoint():为当前视图添加红点
func showRedPoint() {
    let redV = UIView(frame: CGRect(x: self.width-8, y: 0, width: 8, height: 8))
    redV.backgroundColor = UIColor.hexString("FF3030")
    addSubview(redV)
    redV.layer.cornerRadius = 4
    redV.clipsToBounds = true
    redV.tag = 1023
}
hiddenRedPoint():移除红点视图
func hiddenRedPoint() {
    let redV = self.viewWithTag(1023)
    if redV != nil {
        redV?.removeFromSuperview()
    }
}

10、CALayer 的扩展

public extension CALayer
setGradient:设置渐变色
///colors: 渐变颜色数组
///locations: 逐个对应渐变色的数组,设置颜色的渐变占比,nil则默认平均分配
///startPoint: 开始渐变的坐标(控制渐变的方向),取值(0 ~ 1)
///endPoint: 结束渐变的坐标(控制渐变的方向),取值(0 ~ 1)
public func setGradient(colors: [UIColor], locations: [NSNumber]? = nil, startPoint: CGPoint, endPoint: CGPoint) {
    let gradientLayer = CAGradientLayer()
    self.insertSublayer(gradientLayer , at: 0)

    var colorArr = [CGColor]()
    for color in colors {
        colorArr.append(color.cgColor)
    }
    CATransaction.begin()
    CATransaction.setDisableActions(true)
    gradientLayer.frame = self.bounds
    CATransaction.commit()
    gradientLayer.colors     = colorArr
    gradientLayer.locations  = locations
    gradientLayer.startPoint = startPoint
    gradientLayer.endPoint   = endPoint
}

11、UIWindow 的扩展

public extension UIWindow
keyWindow:获取视图窗口
static var keyWindow: UIWindow? {
    var window:UIWindow? = nil
    if #available(iOS 13.0, *) {
        for windowScene:UIWindowScene in ((UIApplication.shared.connectedScenes as?  Set)!) {
            if windowScene.activationState == .foregroundActive {
                window = windowScene.windows.first
                break
            }
        }
        return window
    } else {
        return  UIApplication.shared.keyWindow
    }
}
currentViewController():获取当前控制器

如果我们为了某个功能单独封装了一个独立的类,我们就希望这个类尽可能独立,从而减少对于外部的依赖。比如我们想要单独封装一个获取通讯录的类,必须要有一个控制器可以present出来一个ABPeoplePickerNavigationController,当然我们可以通过外部传入当前的控制器,可是总觉得很别扭,那么怎么能在类内部获取当前正在显示的控制器呢?

虽然我们不能直接获取当前正在显示的控制器,可是每个应用只有一个主窗口,我们可以获取这个UIWindow对象,然后通过一定的方法遍历到当前控制器,而keyWindow只有一个rootViewController,这个控制器要么是UITabBarController或者其子类,要么是UINavigationController或者其子类,要么是UIViewController或者其子类,我们暂且称其为A,而后出现的控制器都是由它们pushpresent出来的,然后就可以递归了。下面就让我们来进行分类讨论:

  • 假如A是UITabbarController或者其子类,那么我们就可以很容易地通过selectedViewController属性很容易地找到下一级控制器
  • 假如AUINavigationController或者其子类,那么我们可以通过visibleViewController属性来获取该控制器push出来的最后一级控制器
  • 假如AUIViewController或者其子类,那么该控制器想要展示出来一个控制器,只能通过present的方式来展现出新的控制器,所以我们可以通过presentedViewController不为空来获取下一级控制器,如果为空则已经是在显示的控制器
func currentViewController() -> (UIViewController?) {
   var window = UIApplication.shared.keyWindow
   if window?.windowLevel != UIWindow.Level.normal{
     let windows = UIApplication.shared.windows
     for  windowTemp in windows{
       if windowTemp.windowLevel == UIWindow.Level.normal{
          window = windowTemp
          break
        }
      }
    }
   let vc = window?.rootViewController
   return currentViewController(vc)
}
func currentViewController(_ vc :UIViewController?) -> UIViewController? {
   if vc == nil {
      return nil
   }
   if let presentVC = vc?.presentedViewController {
      return currentViewController(presentVC)
   }
   else if let tabVC = vc as? UITabBarController {
      if let selectVC = tabVC.selectedViewController {
          return currentViewController(selectVC)
       }
       return nil
    }
    else if let naiVC = vc as? UINavigationController {
       return currentViewController(naiVC.visibleViewController)
    }
    else {
       return vc
    }
 }

你可能感兴趣的:(Swift:UIKit 扩展工具箱)