原創:問題解決型文章
創作不易,請珍惜,之后會持續更新,不斷完善
個人比較喜歡做筆記和寫總結,畢竟好記性不如爛筆頭哈哈,這些文章記錄了我的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
,而后出現的控制器都是由它們push
,present
出來的,然后就可以遞歸了。下面就讓我們來進行分類討論:
- 假如A是
UITabbarController
或者其子類,那么我們就可以很容易地通過selectedViewController
屬性很容易地找到下一級控制器 - 假如
A
是UINavigationController
或者其子類,那么我們可以通過visibleViewController
屬性來獲取該控制器push
出來的最后一級控制器 - 假如
A
是UIViewController
或者其子類,那么該控制器想要展示出來一個控制器,只能通過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
}
}