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<integer_t>.size)
        let kerr = withUnsafeMutablePointer(to: &info) { infoPtr in
            return infoPtr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { (machPtr: UnsafeMutablePointer<integer_t>) 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<UIWindowScene>)!) {
            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
    }
 }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。

推薦閱讀更多精彩內容

  • 原創:問題解決型文章創作不易,請珍惜,之后會持續更新,不斷完善個人比較喜歡做筆記和寫總結,畢竟好記性不如爛筆頭哈哈...
    時光啊混蛋_97boy閱讀 418評論 0 0
  • iOS開發系列--網絡開發 概覽 大部分應用程序都或多或少會牽扯到網絡開發,例如說新浪微博、微信等,這些應用本身可...
    lichengjin閱讀 3,707評論 2 7
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,135評論 1 32
  • 1、禁止手機睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa閱讀 1,142評論 1 6
  • 轉自:http://www.lxweimin.com/p/10b2323f502e 1、禁止手機睡眠 [UIApp...
    aggie1024閱讀 2,686評論 0 6