原创:问题解决型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的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
,而后出现的控制器都是由它们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
}
}