24个好用的Extension让代码更整洁(转自Medium)
扩展(Extension)是swift和oc最好用的功能之一,能够无侵入的对现有系统类和工程添加新方法
作为一个iOS和Android的双料开发者,我经常能看到Android代码的功能方法更简洁,易懂;
当然通过扩展,我们也可以把这些特性使用在swift上,使swift项目也更精炼,更加好用
以下代码是基于swift,但其实对于oc,大部分也可以很容易的移植
1.String.trim() and Swift.trimmed 我们经常会用到string的trim功能,帮助把string里的空格,制表符,换行符等特殊符号去掉
扩展可以这么写
1 2 3 4 5 6 7 8 9 10 11 import Foundationextension String { var trimmed: String { self .trimmingCharacters(in : .whitespacesAndNewlines) } mutating func trim () { self = self .trimmed } }
用法:
1 2 3 var str1 = " a b c d e \n" var str2 = str1 .trimmedstr1 .trim()
2. Int.toDouble() and Double.toInt() Int和Double,CGFloat的转换是项目中经常会用到.但实际使用上遇到Double(a),必须要求a是非可选类型,所以经常要做额外处理
可以对Int和Double的扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 import Foundationextension Int { func toDouble () -> Double { Double (self ) } } extension Double { func toInt () -> Int { Int (self ) } }
用法:
1 2 let a = 15.78 let b = a.toInt()
3. String.toDate(…) and Date.toString(…) 对Date和String的转换做易用的封装
扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import Foundationextension String { func toDate (format: String) -> Date? { let df = DateFormatter () df.dateFormat = format return df.date(from: self ) } } extension Date { func toString (format: String) -> String { let df = DateFormatter () df.dateFormat = format return df.string(from: self ) } }
用法:
1 2 3 let strDate = "2020-08-10 15:00:00" let date = strDate.toDate(format: "yyyy-MM-dd HH:mm:ss" )let strDate2 = date?.toString (format: "yyyy-MM-dd HH:mm:ss" )
4.Int.fenToYuan() 没什么可说的,金额的分转元
扩展:
1 2 3 4 5 6 7 8 import Foundationextension Int { func fenToYuan () -> Double { Double (self ) / 100 } }
用法:
1 2 let fen = 12350 let yuan = fen.fenToYuan()
5.String.asCoordinates() string转成坐标,包括两个值,精度和纬度,在3D场景下还有高度数据,高度不是很常用
我们根据string里的逗号来解析,转成CLLocationCoordinate2D对象
扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import Foundationimport CoreLocationextension String { var asCoordinates: CLLocationCoordinate2D? { let components = self .components(separatedBy: "," ) if components.count != 2 { return nil } let strLat = components[0 ].trimmed let strLng = components[1 ].trimmed if let dLat = Double (strLat), let dLng = Double (strLng) { return CLLocationCoordinate2D (latitude: dLat, longitude: dLng) } return nil } }
用法:
1 2 let strCoordinates = "41.6168, 41.6367" let coordinates = strCoordinates.asCoordinates
6.String.asURL() URL是用处理链接的常用类,很灵活,可以连接不同组建,可以把各种类型处理为URLs,同时,也经常会配合着string的使用
扩展:
1 2 3 4 5 6 7 import Foundationextension String { var asURL: URL? { URL (string: self ) } }
用法:
1 2 let strUrl = "https://medium.com" let url = strUrl.asURL
7.UIDevice.vibrate() iPhone的震动效果是手机行业内做的最好的,可以主动调用这个
扩展:
1 2 3 4 5 6 7 8 import UIKitimport AudioToolboxextension UIDevice { static func vibrate () { AudioServicesPlaySystemSound (1519 ) } }
用法:
8.String.width(…) and String.height(…) 计算UILabel里的text占用的宽度,根据Font计算
扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import UIKitextension String { func height (withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat { let constraintRect = CGSize (width: width, height: .greatestFiniteMagnitude) let boundingBox = self .boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil ) return ceil(boundingBox.height) } func width (withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat { let constraintRect = CGSize (width: .greatestFiniteMagnitude, height: height) let boundingBox = self .boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil ) return ceil(boundingBox.width) } } extension NSAttributedString { func height (withConstrainedWidth width: CGFloat) -> CGFloat { let constraintRect = CGSize (width: width, height: .greatestFiniteMagnitude) let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil ) return ceil(boundingBox.height) } func width (withConstrainedHeight height: CGFloat) -> CGFloat { let constraintRect = CGSize (width: .greatestFiniteMagnitude, height: height) let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil ) return ceil(boundingBox.width) } }
用法:
1 2 let text = "Hello, world!" let textHeight = text.height(withConstrainedWidth: 100 , font: UIFont .systemFont(ofSize: 16 ))
9.String.containsOnlyDigits 检查string是否只含有数字
扩展:
1 2 3 4 5 6 7 8 import Foundationextension String { var containsOnlyDigits: Bool { let notDigits = NSCharacterSet .decimalDigits.inverted return rangeOfCharacter(from: notDigits, options: String .CompareOptions .literal, range: nil ) == nil } }
用法:
1 2 let digitsOnlyYes = "1234567890" .containsOnlyDigitslet digitsOnlyNo = "12345+789" .containsOnlyDigits
10.String.isAlphanumeric 检查string是否只有数字和大小写字母,经常用于密码格式验证
扩展
1 2 3 4 5 6 7 import Foundationextension String { var isAlphanumeric: Bool { !isEmpty && range(of: "[^a-zA-Z0-9]" , options: .regularExpression) == nil } }
用法:
1 2 let alphanumericYes = "asd3kJh43saf" .isAlphanumericlet alphanumericNo = "Kkncs+_s3mM." .isAlphanumeric
11.String 下标 swift的截取内容是比较繁琐的,比如:获取第n位内容,可以只设置一个Int参数
扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import Foundationextension String { subscript (i: Int ) -> Character { return self [index(startIndex, offsetBy: i)] } subscript (bounds: CountableRange <Int >) -> Substring { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(startIndex, offsetBy: bounds.upperBound) if end < start { return "" } return self [start..<end] } subscript (bounds: CountableClosedRange <Int >) -> Substring { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(startIndex, offsetBy: bounds.upperBound) if end < start { return "" } return self [start...end] } subscript (bounds: CountablePartialRangeFrom <Int >) -> Substring { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(endIndex, offsetBy: -1 ) if end < start { return "" } return self [start...end] } subscript (bounds: PartialRangeThrough <Int >) -> Substring { let end = index(startIndex, offsetBy: bounds.upperBound) if end < startIndex { return "" } return self [startIndex...end] } subscript (bounds: PartialRangeUpTo <Int >) -> Substring { let end = index(startIndex, offsetBy: bounds.upperBound) if end < startIndex { return "" } return self [startIndex..<end] } }
用法:
1 2 let subscript1 = "Hello, world!" [7 ...]let subscript2 = "Hello, world!" [7 ...11 ]
12. UIImage.squared 如果需要用户提交一张方形照片,他们很少有标准的方形照片,但一般App里的头像控件往往都是圆形或者方形的
这个扩展可以快速的做处理
扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import UIKitextension UIImage { var squared: UIImage? { let originalWidth = size.width let originalHeight = size.height var x: CGFloat = 0.0 var y: CGFloat = 0.0 var edge: CGFloat = 0.0 if (originalWidth > originalHeight) { edge = originalHeight x = (originalWidth - edge) / 2.0 y = 0.0 } else if (originalHeight > originalWidth) { edge = originalWidth x = 0.0 y = (originalHeight - originalWidth) / 2.0 } else { edge = originalWidth } let cropSquare = CGRect (x: x, y: y, width: edge, height: edge) guard let imageRef = cgImage?.cropping(to: cropSquare) else { return nil } return UIImage (cgImage: imageRef, scale: scale, orientation: imageOrientation) } }
以上是类方法,也可以改为成实例方法
用法:
1 2 let img = UIImage () let imgSquared = img.squared
13.UIImage.resized(…) 图片资源上传服务器的时候,会有大小限制,往往会处理成一张小尺寸照片
扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import UIKitextension UIImage { func resized (maxSize: CGFloat) -> UIImage? { let scale: CGFloat if size.width > size.height { scale = maxSize / size.width } else { scale = maxSize / size.height } let newWidth = size.width * scale let newHeight = size.height * scale UIGraphicsBeginImageContext (CGSize (width: newWidth, height: newHeight)) draw(in : CGRect (x: 0 , y: 0 , width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext () UIGraphicsEndImageContext () return newImage } }
用法
1 2 let img2 = UIImage () let img2Thumb = img2.resized(maxSize: 512 )
可以结合第12条一起使用
1 2 let img = UIImage () let imgPrepared = img.squared?.resized(maxSize: 512 )
14.Int.toString() 一般可以用”‘\(num)”的方法把Int类型转为string,但是有个问题,如果你的num是可选类型,则会把”optional”也加进去,Kotlin处理可选类型比较优雅:对任意类型someVar?.toString,返回可选string
可惜,swift不允许对Any扩展,但至少可以给Int加上
扩展
1 2 3 4 5 6 7 import Foundationextension Int { func toString () -> String { "\(self)" } }
用法
1 2 let i1 = 15 let i1AsString = i1.toString ()
15.Double.toString() 和14同理,对Double加toString方法,但限制是保留两位小数
扩展:
1 2 3 4 5 6 7 import Foundationextension Double { func toString () -> String { String (format: "%.02f" , self ) } }
用法:
1 2 let d1 = 15.67 let d1AsString = d1.toString ()
16.Double.toPrice() 用的地方不多,转成”323,232,21.33$”这种格式
扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import Foundationextension Double { func toPrice (currency: String) -> String { let nf = NumberFormatter () nf.decimalSeparator = "," nf.groupingSeparator = "." nf.groupingSize = 3 nf.usesGroupingSeparator = true nf.minimumFractionDigits = 2 nf.maximumFractionDigits = 2 return (nf.string(from: NSNumber (value: self )) ?? "?" ) + currency } }
用法:
1 2 let dPrice = 16.50 let strPrice = dPrice.toPrice(currency: "€" )
17.String.asDict 把JSON格式的string拆成字典,swift很方便
扩展:
1 2 3 4 5 6 7 8 9 import Foundationextension String { var asDict: [String : Any ]? { guard let data = self .data(using: .utf8) else { return nil } return try ? JSONSerialization .jsonObject(with: data, options: .allowFragments) as ? [String : Any ] } }
用法:
1 2 let json = "{\"hello\": \"world\"}" let dictFromJson = json.asDict
18.String.asArray 和上个类似,转成集合类型
扩展:
1 2 3 4 5 6 7 8 import Foundationextension String { var asArray: [Any ]? { guard let data = self .data(using: .utf8) else { return nil } return try ? JSONSerialization .jsonObject(with: data, options: .allowFragments) as ? [Any ] } }
用法:
1 2 let json2 = "[1, 2, 3]" let arrFromJson2 = json2.asArray
19.String.asAttributedString 有时我们需要一些跨平台的文本格式,最常用的是HTML格式,UILabel可以对粗体,下划线等要是做处理.把HTML格式转成NSAttributedString格式,然后给UILabel.attributedText赋值使用
扩展:
1 2 3 4 5 6 7 8 import Foundationextension String { var asAttributedString: NSAttributedString? { guard let data = self .data(using: .utf8) else { return nil } return try ? NSAttributedString (data: data, options: [.documentType: NSAttributedString .DocumentType .html], documentAttributes: nil ) } }
用法:
1 2 let htmlString = "<p>Hello, <strong>world!</string></p>" let attrString = htmlString.asAttributedString
20.Bundle.appVersion 可以从Info.plist里加载app的版本信息,用途有:
获取版本信息
检查是否有更新
展示版本信息
支持邮件里包含版本信息
扩展:
1 2 3 4 5 6 7 8 9 10 11 import Foundationextension Bundle { var appVersion: String? { self .infoDictionary?["CFBundleShortVersionString" ] as ? String } static var mainAppVersion: String? { Bundle .main.appVersion } }
用法:
1 let appVersion = Bundle .mainAppVersion
原文:
https://medium.com/better-programming/24-swift-extensions-for-cleaner-code-41e250c9c4c3
原文确实只有20个…