最近Swift项目实践的小分享

最近整理了一些我自己在Swift项目实践中的小小工具类,今天把它们溜出来晒晒太阳,有错误的地方也麻烦大哥们指出,共同进步,共同学习

  • 图片下载器:借用Kingfisher中的下载方法,实现图片的批量下载,顺序传入图片url,按原顺序传出
//图片下载回调 resultArr:每张图片下载的状态   resultImageArr:图片
typealias BWImageDownloadCompletionHandle = (_ resultArr: [BWImageDownloadModel]) -> Void
//单张图片下载回调
typealias BWSingleImagDownloadCompletionHadnle = (_ imageModel: BWImageDownloadModel) -> Void

/// 图片数据下载工具
class BWImageDownloadManager: NSObject {
    static let shared = BWImageDownloadManager()
    
    /// 批量下载网络图片
    /// - Parameters:
    ///   - sourceImageUrls: 图片链接数据
    ///   - completionHadle: 回调
    open func asyncDownloadImageWith(sourceImageUrls: [String], completionHadle: @escaping BWImageDownloadCompletionHandle) {
        //创建调度组
        let dispatchGroup: DispatchGroup = DispatchGroup.init()
        //创建并发队列
        let concurrentQueue: DispatchQueue = DispatchQueue.init(label: "", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
        //创建字典用于接收下载好的图片model
        var resultImageDic = Dictionary<Int, BWImageDownloadModel>.init()
        if sourceImageUrls.count > 0 {
            for (index, url) in sourceImageUrls.enumerated() {
                var imageUrl: String = url
                if imageUrl.contains(" ") {
                    //清除urlPath中的空格
                    imageUrl = imageUrl.replacingOccurrences(of: " ", with: "")
                }
//                if imageUrl.count <= 0 {
//                   assert(false, "传入的url不能为空字符串")
//                }
                //对url进行编码
                imageUrl = imageUrl.urlStringEncodingByURLQueryAllowed()
                dispatchGroup.enter()
                concurrentQueue.async {
                    ImageDownloader.default.downloadImage(with: URL(string: imageUrl)!, retrieveImageTask: nil, options: nil, progressBlock: { (a, b) in
                        
                    }) { (image, error, url, imageData) in
                        if error != nil {
                            BWLog("下标为\(index) 图片链接为\(imageUrl)的图片下载失败 失败原因:\(error!)")
                        }
                        let imageModel = self.initDownloadImageModel(image: image, error: error, url: url, imageData: imageData)
                        //图片传入的顺序作为key 下载好的图片作为value 保证最终传出的图片数据顺序与传入一致
                        resultImageDic[index] = imageModel
                        dispatchGroup.leave()
                    }                    
                }
            }
        }
        //等待线程通知
        dispatchGroup.notify(queue: DispatchQueue.main) {
            var resultImageArr = Array<BWImageDownloadModel>.init()
            for (index,_) in sourceImageUrls.enumerated() {
                resultImageArr.append(resultImageDic[index]!)
            }
            completionHadle(resultImageArr)
        }
    }
    
    /// 单张图片下载
    /// - Parameters:
    ///   - sourceImageUrl: 图片链接
    ///   - completionHandle: 回调
    open func downloadSingleImageWith(sourceImageUrl: String?, completionHandle: @escaping BWSingleImagDownloadCompletionHadnle) {
        var imageUrl = sourceImageUrl ?? ""
        if imageUrl.contains(" ") {
            //清除urlPath中的空格
            imageUrl = imageUrl.replacingOccurrences(of: " ", with: "")
        }
        //对链接进行编码
        imageUrl = sourceImageUrl!.urlStringEncodingByURLQueryAllowed()
        ImageDownloader.default.downloadImage(with: URL(string: imageUrl)!, retrieveImageTask: nil, options: nil, progressBlock: { (a, b) in
            
        }) { (image, error, url, imageData) in
            let imageModel = self.initDownloadImageModel(image: image, error: error, url: url, imageData: imageData)
            completionHandle(imageModel)
        }
    }
    
    //创建图片Model
    internal func initDownloadImageModel(image: UIImage?, error: NSError?, url: URL?, imageData: Data?) -> BWImageDownloadModel {
        let imageModel = BWImageDownloadModel.init()
        imageModel.image = image
        imageModel.error = error
        imageModel.url = url
        imageModel.imageData = imageData
        return imageModel
    }
}

extension BWImageDownloadManager {
    /// Kingfisher 清空所有缓存
    open func clearImageCache() {
        //清空硬盘缓存
        self.clearDiskCache()
        //清空网络缓存
        self.clearMemoryCache()
        //清理过期的,或者超过硬盘限制大小的
        self.clearExpiredDiskCache()
    }
    
    /// 清空硬盘缓存
    open func clearDiskCache() {
        KingfisherManager.shared.cache.clearDiskCache()
    }
    
    /// 清空网络缓存
    open func clearMemoryCache() {
        KingfisherManager.shared.cache.clearMemoryCache()
    }
    
    /// 清空过期的或者超过硬盘限制大小的缓存
    open func clearExpiredDiskCache() {
        KingfisherManager.shared.cache.cleanExpiredDiskCache()
    }
}

/// 图片下载model
class BWImageDownloadModel: NSObject {
    var image: UIImage?
    var error: NSError?
    var url: URL?
    var imageData: Data?
}

  • HUD: 简单封装了SVProgressHUD 见笑了见笑了,这里特别指出,导入SVProgressHUD后,因为SVProgressHUD工具内部从Bundle获取不到的各状态图标,所以各状态的图标我在外部传入,相信大家在自己的项目中,一般也不会用到工具中默认的状态图标,都会根据UI设计的重新设定
/// HUD基础配置
    open class func configProgressHUD() {
        SVProgressHUD.setMinimumDismissTimeInterval(1.2)
        SVProgressHUD.setShouldTintImages(false)//阻止图片被渲染的花里胡哨
        SVProgressHUD.setCornerRadius(5)
    }
    
    /// 展示加载HUD
    open class func showLoadding() {
        SVProgressHUD.setForegroundColor(UIColor.white)
        SVProgressHUD.setBackgroundColor(UIColor.black)
        SVProgressHUD.show()
    }
    
    /// 隐藏HUD  延迟0.4s执行 避免网络状态良好 菊花闪退效果
    open class func hiddenLoadding() {
        DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
            SVProgressHUD.dismiss()
        }
    }
    
    /// 仅展示文字
    ///
    /// - Parameter hint: 提示文字
    open class func onlyShowHint(hint: String) {
        SVProgressHUD.setForegroundColor(UIColor.white)
        SVProgressHUD.setBackgroundColor(UIColor.black)
        //需要将SVProgressHUD源码中的setInfoImage的image参数nonnull变成nullable
        SVProgressHUD.setInfoImage(UIImage.init(named: ""))
        SVProgressHUD.showInfo(withStatus: hint)
    }
    
    /// 展示错误信息
    ///
    /// - Parameter hint: 错误信息
    open class func showErrorWithHint(_ hint: String) {
        SVProgressHUD.setForegroundColor(UIColor.white)
        SVProgressHUD.setBackgroundColor(UIColor.black)
        SVProgressHUD.setErrorImage(UIImage.init(named: "HUD_Error") ?? UIImage.init())
        SVProgressHUD.showError(withStatus: hint)
    }
    
    /// 展示成功信息
    ///
    /// - Parameter hint: 成功信息
    open class func showSuccessWithHint(_ hint: String) {
        SVProgressHUD.setForegroundColor(UIColor.white)
        SVProgressHUD.setBackgroundColor(UIColor.black)
        SVProgressHUD.setSuccessImage(UIImage.init(named: "HUD_Success") ?? UIImage.init())
        SVProgressHUD.showSuccess(withStatus: hint)
    }
  • Alamofire的二次封装:这个在之前文章写过了 就不多写了 直接走你 传送门

  • 数据归档解档

/// 数据归档
    ///
    /// - Parameters:
    ///   - targetData: 归档数据
    ///   - path: 归档路径
    class func keyedArchiver(targetData: Any, path: String) {
        if path.isEmpty {
            NSLog("当前路径不存在")
            return
        }
        let localPath: String = self.archiveLocalPath(path: path)
        NSKeyedArchiver.archiveRootObject(targetData, toFile: localPath);
    }
    
    /// 数据解档
    ///
    /// - Parameter path: 存储路径
    /// - Returns: 解档数据
    class func keyedUnArchiver(path: String) -> Any {
        let localPath: String = self.archiveLocalPath(path: path)
        let objc: AnyObject = NSKeyedUnarchiver.unarchiveObject(withFile: localPath) as AnyObject
        return objc
    }
    
    
    /// 本地存储的路径
    ///
    /// - Parameter path: 传入路径
    /// - Returns: 返回完整路径
    class func archiveLocalPath(path: String) -> String {
        let documentPath: String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        let localPath: String = String.init(format: "%@/%@", documentPath,path)
        NSLog("保存到本地的路径:%@", localPath)
        return localPath
    }
  • 自定义UITextView以及UITextField实现输入字数限制Swift版本 代码贴的有点长了 直接简简单单链接走起来 Swift版本 这个是从早先的OC版本延伸出来的 OC版本也贴一个吧 OC版本

  • 简单的对HandyJson的一些数据转模型或者模型转数据的方法做了一点简单的封装,便于HandyJson改版更新后,自己项目能够尽量少的动一些刀子,Kingfisher也有类似的封装,这里就不贴出Kingfisher相应的代码了

/// json处理
class BWJsonUtil<T: HandyJSON>: NSObject {
    
    /// jsonDic -> model
    ///
    /// - Parameter jsonDic: 字典
    /// - Returns: 自定义model
    static func modelFromJsonDic(_ jsonDic: [String: Any]) -> T? {
        if jsonDic.count == 0 {
            BWLog("传入的json字典为空")
            return nil
        }
        return JSONDeserializer<T>.deserializeFrom(dict: jsonDic)
    }
    
    /// jsonDic -> 自定义model
    ///
    /// - Parameters:
    ///   - jsonDic: json字符串
    ///   - designatedPath: 路由
    /// - Returns: 自定义模型
    static func modelFromJsonDic(_ jsonDic: [String: Any], designatedPath: String) -> T? {
        return JSONDeserializer<T>.deserializeFrom(dict: jsonDic, designatedPath: designatedPath)
    }
    
    /// jsonArray -> 自定义模型数组
    ///
    /// - Parameter jsonArr: json数组
    /// - Returns: 自定义属性数组
    static func modelArrayFromJsonArray(_ jsonArr: [Any]) -> [T]? {
        if jsonArr.isEmpty == true {
            BWLog("传入的json数组为空")
            return nil
        }
        return JSONDeserializer<T>.deserializeModelArrayFrom(array: jsonArr) as? [T]
    }
    
    /// jsonString -> model
    /// - Parameters:
    ///   - jsonString: json字符串
    static func modelFromJsonString(_ jsonString: String) -> T? {
        if jsonString == "" || jsonString.count == 0 {
            BWLog("传入的json字符串为空");
            return nil
        }
        return JSONDeserializer<T>.deserializeFrom(json: jsonString)
    }
    
    /// JsonString -> model
    ///
    /// - Parameters:
    ///   - jsonString: jsonString
    ///   - designatedPath: 路由
    /// - Returns: 自定义模型
    static func modelFromJsonString(_ jsonString: String, designatedPath: String) -> T? {
        return JSONDeserializer<T>.deserializeFrom(json: jsonString, designatedPath: designatedPath)
    }
    
    /// json字符串 -> 自定义模型数组
    /// - Parameters:
    ///   - jsonString: json字符串
    ///   - modelType: 自定义模型类型
    static func modelArrayFromJsonString(_ jsonString: String) -> [T]? {
        if jsonString == "" || jsonString.count == 0 {
            BWLog("传入的json字符串为空")
            return nil
        }
        return JSONDeserializer<T>.deserializeModelArrayFrom(json: jsonString) as? [T]
    }
    
    /// jsonString -> 自定义模型数组
    ///
    /// - Parameters:
    ///   - jsonString: json字符串
    ///   - designatedPath: 路由
    /// - Returns: 自定义模型数组
    static func modelArrayFromJsonString(_ jsonString: String, designatedPath: String) -> [T]? {
        return JSONDeserializer<T>.deserializeModelArrayFrom(json: jsonString, designatedPath: designatedPath) as? [T]
    }
    
    /// 自定义模型 -> json字符串
    /// - Parameter model: 自定义模型
    static func jsonStringFromModel(_ model: T?) -> String {
        if model == nil {
            BWLog("传入的模型为nil")
            return ""
        }
        return (model?.toJSONString())!
    }
    
    /// 自定义模型 -> 字典
    /// - Parameter model: 模型
    static func jsonDicFromModel(_ model: T?) -> [String: Any] {
        if model == nil {
            BWLog("传入的模型为nil")
            return [:]
        }
        return (model?.toJSON())!
    }
}
  • 三级联动地址选择器:在之前的文章也有过展出,就不单独贴代码了,直接走你

  • 对SnapKit的一个小小封装 哈哈哈 说是封装 其实就是能让布局代码少一个snp 少一个也是少嘛 尤其是我这种从OC转过来的,用习惯了Masonry

extension UIView {
    /// 新建约束
    /// - Parameter closure: 约束
    public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
        self.snp.makeConstraints(closure)
    }
    
    /// 删除原先约束并新建约束
    /// - Parameter closure: 新的约束
    public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
        self.snp.remakeConstraints(closure)
    }
    
    /// 更新约束
    /// - Parameter closure: 新的约束
    public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
        self.snp.updateConstraints(closure)
    }
}
  • 导航栏底部横线的隐藏以及展示 & UIToolBar横线的隐藏以及展示
extension UINavigationBar {
    
    //隐藏导航栏底部横线
    func hideBottomHairline() {
        let bottomImageV = hairlineImageViewInNavigationBar(view: self)
        bottomImageV?.isHidden = true
    }
    
    //展示导航栏底部横线
    func showBottomHairLine() {
        let bottomImageV = hairlineImageViewInNavigationBar(view: self)
        bottomImageV?.isHidden = false
    }
    
    //找出导航栏底部的那根线
    private func hairlineImageViewInNavigationBar(view: UIView) -> UIImageView? {
        if view.isKind(of: UIImageView.self) && view.height <= 1.0 {
            return view as? UIImageView
        }
        for subView in view.subviews {
            if let imageView = hairlineImageViewInNavigationBar(view: subView) {
                return imageView
            }
        }
        return nil
    }
}

extension UIToolbar {
    
    //隐藏
    func hideHairline() {
        let toolBarImageView = hairlinImageViewInToolBar(view: self)
        toolBarImageView?.isHidden = true
    }
    
    //展示
    func showHairline() {
        let toolBarImageView = hairlinImageViewInToolBar(view: self)
        toolBarImageView?.isHidden = false
    }
    
    private func hairlinImageViewInToolBar(view: UIView) -> UIImageView? {
        if view.isKind(of: UIImageView.self) && view.height <= 1.0 {
            return view as? UIImageView
        }
        for subView in view.subviews {
            if let imageView = hairlinImageViewInToolBar(view: subView) {
                return imageView
            }
        }
        return nil
    }
}
  • MJRefresh的便捷使用:对UIScrollView扩展方法
extension UIScrollView {
    /// 添加刷新头
    ///
    /// - Parameter handle: 刷新事件
    /// - Returns: 刷新头
    @discardableResult  //忽略返回值警告
    public func bw_addRefreshHeaderWithHandle(_ handle: @escaping () -> Void) -> MJRefreshStateHeader {
        let header: MJRefreshStateHeader = MJRefreshStateHeader.init(refreshingBlock: handle)
        header.stateLabel.font = BWSystemRegularFont(fontSize: 14)
        header.stateLabel.textColor = color999999
        self.mj_header = header
        return self.mj_header as! MJRefreshStateHeader
    }
    
    /// 添加刷新尾
    ///
    /// - Parameter handle: 刷新事件
    /// - Returns: 刷新尾
    @discardableResult
    public func bw_addPagingRefreshFooterWithHandle(_ handle: @escaping () -> Void) -> MJRefreshAutoNormalFooter {
        let footer: MJRefreshAutoNormalFooter = MJRefreshAutoNormalFooter.init(refreshingBlock: handle)
        footer.stateLabel.font = BWSystemRegularFont(fontSize: 14)
        footer.stateLabel.textColor = color999999
        self.mj_footer = footer
        return self.mj_footer as! MJRefreshAutoNormalFooter
    }
    
    /// 添加刷新尾
    ///
    /// - Parameters:
    ///   - notice: 没有更多数据时的提示文字
    ///   - handle: 刷新事件
    /// - Returns: 刷新尾
    @discardableResult
    public func bw_addPagingRefreshFooterWithNotice(_ notice: String, handle: @escaping () -> Void) -> MJRefreshAutoNormalFooter {
        let footer = self.bw_addPagingRefreshFooterWithHandle(handle)
        footer.setTitle(notice, for: MJRefreshState.noMoreData)
        return footer
    }
    
    /// 开始刷新
    func bw_startRefreshing() {
        if self.mj_header != nil {
            self.mj_header.beginRefreshing()
        }
        if self.mj_footer != nil {
            self.mj_footer.isHidden = true
        }
        //避免滑到很多页之后重新刷新造成的偏移量太大 滚动效果太明显的问题
        self .setContentOffset(CGPoint.zero, animated: false)
    }
    
    /// 结束刷新
    func bw_endRefreshing() {
        if self.mj_header != nil {
            self.mj_header.endRefreshing()
        }
        if self.mj_footer != nil {
            self.mj_footer.resetNoMoreData()
            self.mj_footer.isHidden = false
        }
    }
    
    /// 没有更多数据了
    func bw_pagingRefreshNoMoreData() {
        if self.mj_footer != nil {
            self.mj_footer.endRefreshingWithNoMoreData()
        }
    }
}
  • 获取Url中的所有参数以及参数值:以key:value字典方式返回
extention String {
    /// 获取url携带的所有参数
    ///
    /// - Returns: 参数
    func allUrlParams() -> Dictionary<String, Any> {
        var urlString: String = self
        var dictionary: Dictionary = [String: Any]()
        if urlString.count > 0 {
            //url编码  去除链接中存在的特殊字符
            urlString = urlString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
            let urlComponents: NSURLComponents = NSURLComponents.init(string: urlString)!
            let urlQuerys: Array = urlComponents.queryItems!
            for items:URLQueryItem in urlQuerys {
                let value = items.value
                dictionary[items.name] = value ?? ""
            }
        }
        return dictionary
    }
}
  • String的扩展方法:
/// 检测邮政编码是否可用
    ///
    /// - Returns: 邮政编码是否可用
    func validatePostalcode() -> Bool {
        let nameRegEx: String = "^[0-8]\\d{5}(?!\\d)$"
        return self.isMatchsRegualExp(string: nameRegEx)
    }
    
    /// 检测url是否可用
    ///
    /// - Returns: url是否可用
    func validateUrl() -> Bool {
        let nameRegEx: String = "^((http)|(https))+:[^\\s]+\\.[^\\s]*$"
        return self.isMatchsRegualExp(string: nameRegEx)
    }
    
    /// 检测身份证号是否有效
    ///
    /// - Returns: 身份证是否有效
    func validateIdentifyCard() -> Bool {
        let nameRegEx: String = "^(\\d{14}|\\d{17})(\\d|[xX])$"
        return self.isMatchsRegualExp(string: nameRegEx)
    }
    
    /// 检测邮箱是否可用
    ///
    /// - Returns: 邮箱是否可用
    func validateEmailAddress() -> Bool {
        let nameRegEx: String = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
        return self.isMatchsRegualExp(string: nameRegEx)
    }
    
    /// 检测手机号是否可用
    ///
    /// - Returns: 手机号是否可用
    func validateMobilNumber() -> Bool {
        /**
         * 手机号码
         * 移动:134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188,1705
         * 联通:130,131,132,152,155,156,185,186,1709
         * 电信:133,1349,153,180,189,1700
         */
        /**
         10         * 中国移动:China Mobile
         11         * 134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188,1705
         12         */
        let CM: String = "^1(34[0-8]|(3[5-9]|5[017-9]|8[278])\\d|705)\\d{7}$"
        /**
         15         * 中国联通:China Unicom
         16         * 130,131,132,152,155,156,185,186,1709
         17         */
        let CU: String = "^1((3[0-2]|5[256]|8[56])\\d|709)\\d{7}$"
        /**
         20         * 中国电信:China Telecom
         21         * 133,1349,153,180,189,1700
         22         */
        let CT: String = "^1((33|53|8[09])\\d|349|700)\\d{7}$"
        /**
         25         * 大陆地区固话及小灵通
         26         * 区号:010,020,021,022,023,024,025,027,028,029
         27         * 号码:七位或八位
         28         */
        let PHS: String = "^0(10|2[0-5789]|\\d{3})\\d{7,8}$"
        if self.isMatchsRegualExp(string: CM) == true || self.isMatchsRegualExp(string: CU) == true || self.isMatchsRegualExp(string: CT) == true || self.isMatchsRegualExp(string: PHS) {
            return true
        }
        return false
    }
    
    /// 谓词过滤
    /// - Parameter string: 被检测的字符串
    func isMatchsRegualExp(string: String) -> Bool {
        let predicate: NSPredicate = NSPredicate.init(format: "SELF MATCHES %@", string)
        return predicate.evaluate(with: self)
    }
    
    /// 对url进行编码 防止出现中文或者其余特殊字符不能被识别的情况
    public func urlStringEncodingByURLQueryAllowed() -> String {
        let urlString: String = self
        return urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
    }
  /// 避免float或者double类型的参数在解析时丢失精度
    ///
    /// - Returns: 没有丢失精度的double
    func doubleFromDecimalString() -> Double {
        let num: Double = Double(self)!
        let string: String = String(format: "%lf", num)
        return NSDecimalNumber.init(string: string).doubleValue
    }
    
    /// double字符串四舍五入
    ///
    /// - Returns: 四舍五入后得到的字符串
    func decimalStringFromRound() -> String {
        let roundUp: NSDecimalNumberHandler = NSDecimalNumberHandler.init(roundingMode: .plain, scale: 2, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true)
        var number: NSDecimalNumber = NSDecimalNumber.init(string: self)
        number = number.rounding(accordingToBehavior: roundUp)
        let roundString: String = String(format: "%.2f", number.doubleValue)
        return roundString
    }  
  • 便捷设置UIButton图片与文字位置与间隙
/// 按钮图片位置枚举
///
/// - left: 图片在左
/// - right: 图片在右
/// - top: 图片在上
/// - bottom: 图片在下
enum BWButtonImagePosition {
    case left
    case right
    case top
    case bottom
}
extension UIButton {
    
    /// 设置图片位置以及图片与文字之间的间距(如果使用layout布局,需先调用父视图的layoutIfNeeded)
    ///
    /// - Parameters:
    ///   - positon: 图片位置
    ///   - spacing: 图片间隙
    func buttonImagePosition(_ positon: BWButtonImagePosition, spacing: CGFloat) {
        //图片视图宽高
        let imageWidth = self.imageView?.width ?? 0.0
        let imageHeight = self.imageView?.height ?? 0.0
        //文字 此处不使用(self.titleLabel.bounds.size.width 为 0)
        let titleWidth = self.titleLabel?.text?.boundingRectWithSize(self.size, font: self.titleFont ?? BWSystemRegularFont(fontSize: 17)).width ?? 0.0
        let titleHeight = self.titleLabel?.text?.boundingRectWithSize(self.size, font: self.titleFont ?? BWSystemRegularFont(fontSize: 17)).height ?? 0.0
        
        //图片中心 X/Y 偏移量
        let imageOffsetX = (imageWidth + titleWidth) / 2.0 - imageWidth / 2.0;
        let imageOffsetY = imageHeight / 2.0 + spacing / 2.0;
        
        //标题中心 X/Y 偏移量
        let width = imageWidth + titleWidth / 2.0
        let halfWidth = (imageWidth + titleWidth) / 2.0
        let titleOffsetX = width - halfWidth
        let titleOffsetY = titleHeight / 2 - spacing / 2
        
        switch positon {
        case .left:
            //图片在左
            self.imageEdgeInsets = UIEdgeInsets.init(top: 0, left: -spacing / 2.0, bottom: 0, right: spacing / 2.0)
            self.titleEdgeInsets = UIEdgeInsets.init(top: 0, left: spacing / 2.0, bottom: 0, right: -spacing / 2.0)
        case .right:
            //图片在右
            self.imageEdgeInsets = UIEdgeInsets.init(top: 0, left: titleWidth + spacing / 2.0, bottom: 0, right: -(titleWidth + spacing / 2.0))
            self.titleEdgeInsets = UIEdgeInsets.init(top: 0, left: -(imageWidth + spacing / 2.0), bottom: 0, right: (imageWidth + spacing / 2.0))
        case .top:
            //图片在上
            self.imageEdgeInsets = UIEdgeInsets.init(top: -(imageOffsetY + spacing / 2.0), left: imageOffsetX, bottom: imageOffsetY + spacing / 2.0, right: -imageOffsetX)
            self.titleEdgeInsets = UIEdgeInsets.init(top: titleOffsetY + spacing / 2.0, left: -titleOffsetX, bottom: -(titleOffsetY + spacing / 2.0), right: titleOffsetX)
        case .bottom:
            //图片在下
            self.imageEdgeInsets = UIEdgeInsets.init(top: imageOffsetY + spacing / 2.0, left: imageOffsetX, bottom: -(imageOffsetY + spacing / 2.0), right: -imageOffsetX)
            self.titleEdgeInsets = UIEdgeInsets.init(top: -(titleOffsetY + spacing / 2.0), left: -titleOffsetX, bottom: titleOffsetY + spacing / 2.0, right: titleOffsetX)
        }
    }
}
  • 扩大UIControl类及其子类的点击返回 44*44
extension UIControl {
    //扩大点击区域  最大为44*44
    override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        var bounds: CGRect = self.bounds;
        //若原热区小于44x44,则放大热区,否则保持原大小不变
        let widthDelta: CGFloat = max(44.0 - bounds.size.width, 0)
        let heightDelta: CGFloat  = max(44.0 - bounds.size.height, 0);
        bounds = bounds.insetBy(dx: -0.5*widthDelta, dy: -0.5*heightDelta)
        let isContain: Bool = bounds.contains(point)
        return isContain;
    }
}
  • 便捷设置UIButton各个状态的图片
extension UIButton {
    //MARK: 设置图片
    
    /// 设置normal状态下带有默认图片的图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setImageForNormal(url: String?) {
        self.bw_setImageWith(url: url, for: .normal)
    }
    
    /// 设置selected状态下带有默认图片的图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setImageForSelected(url: String?) {
        self.bw_setImageWith(url: url, for: .selected)
    }
    
    /// 设置highlighted状态下带有默认图片的图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setImageForHighlighted(url: String?) {
        self.bw_setImageWith(url: url, for: .highlighted)
    }
    
    /// 设置disabled状态下带有默认图片的图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setImageForDisabled(url: String?) {
        self.bw_setImageWith(url: url, for: .disabled)
    }
    
    /// 设置normal状态下的图片
    /// - Parameters:
    ///   - url: 图片链接|本地图片名称
    ///   - placeholderImage: 占位图
    func bw_setImageForNormal(url: String?, placeholderImage: UIImage?) {
        self.bw_setImageWith(url: url, for: .normal, placeholderImage: placeholderImage)
    }
    
    /// 设置selected状态下的图片
    /// - Parameters:
    ///   - url: 图片链接|本地图片名称
    ///   - placeholderImage: 占位图
    func bw_setImageForSelected(url: String?, placeholderImage: UIImage?) {
        self.bw_setImageWith(url: url, for: .selected, placeholderImage: placeholderImage)
    }
    
    /// 设置highlighted状态下的图片
    /// - Parameters:
    ///   - url: 图片链接|本地图片名称
    ///   - placeholderImage: 占位图
    func bw_setImageForHighlighted(url: String?, placeholderImage: UIImage?) {
        self.bw_setImageWith(url: url, for: .highlighted, placeholderImage: placeholderImage)
    }
    
    /// 设置disabled状态下的图片
    /// - Parameters:
    ///   - url: 图片链接|本地图片名称
    ///   - placeholderImage: 占位图
    func bw_setImageForDisabled(url: String, placeholderImage: UIImage?) {
        self.bw_setImageWith(url: url, for: .disabled, placeholderImage: placeholderImage)
    }
    
    /// 加载图片
    ///
    /// - Parameters:
    ///   - url: 图片链接|本地图片名
    ///   - state: 状态
    func bw_setImageWith(url: String?,for state: UIControl.State) {
        self.bw_setImageWith(url: url, for: state, placeholderImage: defaultPlaceholderImage)
    }
    
    /// 加载图片
    ///
    /// - Parameters:
    ///   - url: 图片链接|本地图片名
    ///   - state: 状态
    ///   - placeholderImage: 占位图片
    func bw_setImageWith(url: String?, for state: UIControl.State,  placeholderImage: UIImage?) {
        if url == nil || url?.count == 0 {
            //url为空或者为空字符串 展示占位图片
            self.setImage(defaultPlaceholderImage, for: state)
            return
        }
        if url?.contains("http") == true {
            //网络图片
            self.kf.setImage(with: URL(string: url!), for: state)
            self.kf.setImage(with: URL(string: url!), for: state, placeholder: placeholderImage)
        } else {
            //本地图片(也有可能是url只有图片的路径 此情况遇到再处理(OC是通过runtime替换加载图片的方法,加入对于图片url的判断,对于缺少host的图片url手动拼接host成为完整的图片路径))
            self.setImage(UIImage(named: url!), for: state)
        }
    }
    
    //MARK: 设置背景图片
    
    /// 设置normal状态下带有默认图片的背景图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setBackgroundImageForNormal(url: String?) {
        self.bw_setBackgroundImageWith(url: url, for: .normal)
    }
    
    /// 设置selected状态下带有默认图片的背景图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setBackgroundImageForSelected(url: String?) {
        self.bw_setBackgroundImageWith(url: url, for: .selected)
    }
    
    /// 设置highlighted状态下带有默认图片的背景图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setBackgroundImageForHighlighted(url: String?) {
        self.bw_setBackgroundImageWith(url: url, for: .highlighted)
    }
    
    /// 设置disabled状态下带有默认图片的背景图片
    /// - Parameter url: 图片链接|本地图片名称
    func bw_setBackgroundImageForDisabled(url: String?) {
        self.bw_setBackgroundImageWith(url: url, for: .disabled)
    }
    
    /// 设置normal状态下的背景图片
    /// - Parameters:
    ///   - url: 图片链接|本地图片名称
    ///   - placeholderImage: 占位图
    func bw_setBackgroundImageForNormal(url: String?, placeholderImage: UIImage?) {
        self.bw_setBackgroundImageWith(url: url, for: .normal, placeholderImage: placeholderImage)
    }
    
    /// 设置selected状态下的背景图片
       /// - Parameters:
       ///   - url: 图片链接|本地图片名称
       ///   - placeholderImage: 占位图
    func bw_setBackgroundImageForSelected(url: String?, placeholderImage: UIImage?) {
        self.bw_setBackgroundImageWith(url: url, for: .selected, placeholderImage: placeholderImage)
    }
    
    /// 设置highlighted状态下的背景图片
       /// - Parameters:
       ///   - url: 图片链接|本地图片名称
       ///   - placeholderImage: 占位图
    func bw_setBackgroundImageForHighlighted(url: String?, placeholderImage: UIImage?) {
        self.bw_setBackgroundImageWith(url: url, for: .highlighted, placeholderImage: placeholderImage)
    }
    
    /// 设置disabled状态下的背景图片
       /// - Parameters:
       ///   - url: 图片链接|本地图片名称
       ///   - placeholderImage: 占位图
    func bw_setBackgroundImageForDisabled(url: String?, placeholderImage: UIImage?) {
        self.bw_setBackgroundImageWith(url: url, for: .disabled, placeholderImage: placeholderImage)
    }
    
    /// 加载背景图片(带默认占位图)
    /// - Parameters:
    ///   - url: 图片链接|本地图片名称
    ///   - state: 状态
    func bw_setBackgroundImageWith(url: String?, for state: UIControl.State) {
        self.bw_setBackgroundImageWith(url: url, for: state, placeholderImage: defaultPlaceholderImage)
    }
    
    /// 加载背景图片
    ///
    /// - Parameters:
    ///   - url: 图片链接|本地图片名
    ///   - state: 状态
    ///   - placeholderImage: 占位图片
    func bw_setBackgroundImageWith(url: String?, for state: UIControl.State, placeholderImage: UIImage?) {
        if url == nil || url?.count == 0 {
            //url为空或者为空字符串 展示占位图片
            self.setBackgroundImage(defaultPlaceholderImage, for: state)
            return
        }
        if url?.contains("http") == true {
            //网络图片
            self.kf.setBackgroundImage(with: URL(string: url!), for: state, placeholder: placeholderImage)
        } else {
            //本地图片
            self.setBackgroundImage(UIImage(named: url!), for: state)
        }
    }
}
  • UIImage的一些扩展方法
extension UIImage {
    
    /// 根据颜色生成纯色图片
    ///
    /// - Parameter color: 颜色
    /// - Returns: 纯色图片
    open class func imageWithColor(color: UIColor) -> UIImage? {
        let rect: CGRect = CGRect.init(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
        UIGraphicsBeginImageContext(rect.size);
        if let context: CGContext = UIGraphicsGetCurrentContext() {
            context.setFillColor(color.cgColor);
            context.fill(rect);
            
            let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext() ?? nil;
            UIGraphicsEndImageContext()
            return image
        }
        return nil
    }
    
    /// 改变纯色图片颜色
    ///
    /// - Parameter color: 颜色
    /// - Returns: 纯色图片
    open func imageWithTintColor(color: UIColor) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
        if let context: CGContext = UIGraphicsGetCurrentContext() {
            context.translateBy(x: 0, y: self.size.height)
            context.scaleBy(x: 1.0, y: -1.0)
            
            context.setBlendMode(CGBlendMode.normal)
            let rect: CGRect = CGRect.init(x: 0, y: 0, width: self.size.width, height: self.size.height)
            context.clip(to: rect)
            color.setFill()
            context.fill(rect)
            let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext() ?? nil;
            UIGraphicsEndImageContext();
            return newImage;
        }
        return nil
    }
    
    /// 获取图片某一像素点的颜色
    /// - Parameter point: 像素点位置
    open func imageColorFrom(point: CGPoint) -> UIColor? {
        if point.x < 0 || point.x > self.size.width || point.y < 0 || point.y > self.size.height {
            return nil
        }
        let provider = self.cgImage!.dataProvider
        let providerData = provider!.data
        let data = CFDataGetBytePtr(providerData)
        
        let numOfComponets: CGFloat = 4
        let pixelData = Int(((self.size.width * point.y) + point.x) * numOfComponets)
         
        let r = CGFloat(data![pixelData]) / 255.0
        let g = CGFloat(data![pixelData + 1]) / 255.0
        let b = CGFloat(data![pixelData + 2]) / 255.0
        let a = CGFloat(data![pixelData + 3]) / 255.0
        
        return UIColor.init(red: r, green: g, blue: b, alpha: a)
    }
    
    /// 按size压缩图片size
    ///
    /// - Parameter targetSize: 目标size
    /// - Returns: 图片
    open func imageByScalingToSize(targetSize: CGSize) -> UIImage? {
        let sourceImage: UIImage = self;
        var newImage: UIImage? = nil;
        let imageSize: CGSize = sourceImage.size;
        let width = imageSize.width;
        let height = imageSize.height;
        let targetWidth = targetSize.width;
        let targetHeight = targetSize.height;
        var scaleFactor: CGFloat = 0.0;
        var scaledWidth = targetWidth;
        var scaledHeight = targetHeight;
        var thumbnailPoint = CGPoint.init(x: 0.0, y: 0.0);
        if imageSize.equalTo(targetSize) == false {
            let widthFactor: CGFloat = CGFloat(targetWidth / width);
            let heightFactor: CGFloat = CGFloat(targetHeight / height);
            if widthFactor < heightFactor {
                scaleFactor = widthFactor;
            } else {
                scaleFactor = heightFactor;
                scaledWidth  = width * scaleFactor;
                scaledHeight = height * scaleFactor;
            }
            // center the image
            if (widthFactor < heightFactor) {
                thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
            } else if (widthFactor > heightFactor) {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
            }
        }
        // this is actually the interesting part:
        UIGraphicsBeginImageContext(targetSize);
        var thumbnailRect: CGRect = CGRect.zero;
        thumbnailRect.origin = thumbnailPoint;
        thumbnailRect.size.width  = scaledWidth;
        thumbnailRect.size.height = scaledHeight;
        sourceImage.draw(in: thumbnailRect)
        newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage ;
    }
}
  • UIView转成UIImage
extension UIView {
    
    /// 按照原尺寸转化成图片
    ///
    /// - Returns: 转化成功的图片
    open func transformToImage() -> UIImage {
        self.layoutSubviews()
        UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0);
        self.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!;
        UIGraphicsEndImageContext();
        return image;
    }
}
  • UIView类通过UIBezierPath画各个位置的圆角,可单独设置圆角位置
/// 添加圆角
    ///
    /// - Parameter cornerRadii: 圆角大小
    open func addAllRoundingCornersWithRadii(cornerRadii: CGSize) {
        self.addBezierPathCorner(roundingCorners: UIRectCorner.allCorners, cornerRadii: cornerRadii)
    }
    
    /// 添加圆角
    ///
    /// - Parameters:
    ///   - roundingCorners: 圆角区域
    ///   - radii: 圆角大小
    open func addBezierPathCorner(roundingCorners: UIRectCorner, cornerRadii radii: CGSize) {
        if superview != nil {
            superview?.layoutIfNeeded()
        }
        let bezierPath: UIBezierPath = UIBezierPath.init(roundedRect: self.bounds, byRoundingCorners: roundingCorners, cornerRadii: radii)
        let shapeLayer: CAShapeLayer = CAShapeLayer.init()
        shapeLayer.frame = self.bounds
        shapeLayer.path = bezierPath.cgPath
        self.layer.mask = shapeLayer
    }
  • UIView添加四边阴影,可选择添加阴影的边
public enum BWShadowPosition {
    case top
    case bottom
    case left
    case right
    case common
    case around
}
/// 添加阴影
    ///
    /// - Parameters:
    ///   - color: 阴影颜色
    ///   - opacity: 阴影透明度
    ///   - radius: 圆角
    ///   - offset: 阴影偏移量
    ///   - width: 阴影宽度
    ///   - position: 添加阴影的位置
    open func shadowPathWithColor(_ color: UIColor, opacity: CGFloat?, radius: CGFloat, offset: CGSize?, width: CGFloat, position: BWShadowPosition) {
        if self.superview != nil {
            self.superview?.layoutIfNeeded()
        }
        //避免阴影被切割隐藏
        self.layer.masksToBounds = false
        self.layer.shadowColor = color.cgColor
        //阴影透明度 默认为0
        self.layer.shadowOpacity = Float(opacity ?? 0.0)
        //阴影便宜量  默认为(0,-3)
        self.layer.shadowOffset = offset ?? CGSize.init(width: 0, height: -3)
        //阴影半径 默认为3
        self.layer.cornerRadius = radius
        
        var shadowRect = CGRect.zero
        let originX: CGFloat = 0
        let originY: CGFloat = 0
        let sizeWidth: CGFloat = self.width
        let sizeHeight: CGFloat = self.height
        switch position {
        case .top: do {
            shadowRect = CGRect.init(x: originX, y: originY - width / 2.0, width: sizeWidth, height: width)
            }
        case .left: do {
            shadowRect = CGRect.init(x: originX - width / 2.0, y: originY, width: width, height: sizeHeight)
            }
        case .bottom: do {
            shadowRect = CGRect.init(x: originY, y: sizeHeight - width / 2.0, width: sizeWidth, height: width)
            }
        case .right: do {
            shadowRect = CGRect.init(x: sizeWidth - width / 2.0, y: originY, width: width, height: sizeHeight)
            }
        case .common: do {
            shadowRect = CGRect.init(x: originX - width / 2.0, y: 2, width: sizeWidth+width, height: sizeHeight + width / 2.0)
            }
        case .around: do {
            shadowRect = CGRect.init(x: originX - width / 2.0, y: originY - width / 2.0, width: sizeWidth + width, height: sizeHeight + width)
            }
        }
        let bezierPath: UIBezierPath = UIBezierPath.init(rect: shadowRect)
        self.layer.shadowPath = bezierPath.cgPath
    }
  • 便捷获取&设置frame的各个属性
extension UIView {
    
    
    public var x: CGFloat {
        set {
            var rect: CGRect = self.frame
            rect.origin.x = newValue
            self.frame = rect
        }
        get {
            return self.frame.origin.x
        }
    }
    public var y: CGFloat {
        set {
            var rect: CGRect = self.frame
            rect.origin.y = newValue
            self.frame = rect
        }
        get {
            return self.frame.origin.y
        }
    }
    public var maxX: CGFloat {
        set {
            self.x = newValue - self.width
        }
        get {
            return self.frame.maxX
        }
    }
    public var maxY: CGFloat {
        set {
            self.y = newValue - self.height
        }
        get {
            return self.frame.maxY
        }
    }
    public var width: CGFloat {
        set {
            var rect: CGRect = self.frame
            rect.size.width = newValue
            self.frame = rect
        }
        get {
            return self.frame.size.width
        }
    }
    public var height: CGFloat {
        set {
            var rect: CGRect = self.frame
            rect.size.height = newValue
            self.frame = rect
        }
        get {
            return self.frame.size.height
        }
    }
    public var centerX: CGFloat {
        set {
            var center: CGPoint = self.center
            center.x = newValue
            self.center = center
        }
        get {
            return self.center.x
        }
    }
    public var centerY: CGFloat {
        set {
            var center: CGPoint = self.center
            center.y = newValue
            self.center = center
        }
        get {
            return self.center.y
        }
    }
    public var size: CGSize {
        set {
            var rect: CGRect = self.frame
            rect.size = newValue
            self.frame = rect
        }
        get {
            return self.frame.size
        }
    }
}
  • NSMutableAttributedString相关的简单封装
typealias attributeString = String

public enum BWStrikethroughStyle {
    case underline              //下划线
    case strikethrough          //中划线
}

extension String {
    
    /// 为String添加中划线|下划线
    ///
    /// - Parameters:
    ///   - color: 颜色
    ///   - lineStyle: 样式
    /// - Returns: 中划线|下划线
    public func addLineAttribteString(color: UIColor, lineStyle: BWStrikethroughStyle) -> NSMutableAttributedString {
        switch lineStyle {
        case .underline: do {
            return self.underlineAttributeString(color: color)
            }
        case .strikethrough: do {
            return self.strikethroughAttributeString(color: color)
            }
        }
    }
    
    /// 添加颜色为#999999的中划线
    ///
    /// - Returns: 中划线
    public func strikethroughAttributeStringForColor999999 () -> NSMutableAttributedString {
        return self.strikethroughAttributeString(color: UIColor.init(hexColorString: "#999999"))
    }
    
    /// 添加中划线
    ///
    /// - Parameter color: 划线颜色
    /// - Returns: NSMutableAttributedString
    public func strikethroughAttributeString(color: UIColor) -> NSMutableAttributedString {
        let strikethroughAttributeString : NSMutableAttributedString = NSMutableAttributedString.init(string: self)
        strikethroughAttributeString.addAttribute(.strikethroughStyle, value: NSNumber.init(value: 1), range: NSRange.init(location: 0, length: strikethroughAttributeString.length))
        strikethroughAttributeString.addAttribute(.strikethroughColor, value: color, range: NSRange.init(location: 0, length: strikethroughAttributeString.length))
        return strikethroughAttributeString
    }
    
    /// 添加下划线
    ///
    /// - Parameter color: 下划线颜色
    /// - Returns: 下划线
    public func underlineAttributeString(color: UIColor) -> NSMutableAttributedString {
        let underlineAttributeString : NSMutableAttributedString = NSMutableAttributedString.init(string: self)
        underlineAttributeString.addAttribute(.underlineStyle, value: NSNumber.init(value: 1), range: NSRange.init(location: 0, length: underlineAttributeString.length))
        underlineAttributeString.addAttribute(.underlineColor, value: color, range: NSRange.init(location: 0, length: underlineAttributeString.length))
        return underlineAttributeString
    }
}

extension String {
    
    /// 字符串后拼接图片
    ///
    /// - Parameters:
    ///   - imageName: 图片名称
    ///   - bounds: 图片bounds
    /// - Returns: 带图片富文本
    public func appendingAttributeStringImageWithImageName(_ imageName: String, bounds: CGRect) -> NSMutableAttributedString {
        let attributeString: NSMutableAttributedString = NSMutableAttributedString.init(string: self)
        //图片处理
        let attachImage: NSTextAttachment = NSTextAttachment.init()
        attachImage.image = UIImage.init(named: imageName)
        attachImage.bounds = bounds
        let attachAttributeString: NSAttributedString = NSAttributedString.init(attachment: attachImage)
        //拼接图片
        attributeString.append(attachAttributeString)
        return attributeString
    }
    
    /// 字符串中插入图片
    ///
    /// - Parameters:
    ///   - imageName: 图片名称
    ///   - bounds: 图片bounds
    ///   - index: 插入的下标
    /// - Returns: 带图片富文本
    public func insertAttributeStringImageWithImageName(_ imageName: String, bounds: CGRect, index: Int) -> NSMutableAttributedString {
        let attributeString: NSMutableAttributedString = NSMutableAttributedString.init(string: self)
        //图片处理
        let attachImage: NSTextAttachment = NSTextAttachment.init()
        attachImage.image = UIImage.init(named: imageName)
        attachImage.bounds = bounds
        let attachAttributeString: NSAttributedString = NSAttributedString.init(attachment: attachImage)
        //拼接图片
        attributeString.insert(attachAttributeString, at: index)
        return attributeString
    }
}
  • 便捷生成UIColor,支持十六进制以及RGB颜色
extension UIColor {
    
    /// 十六进制颜色c转化成UIColor
    ///
    /// - Parameter hexColorString: 十六进制颜色
    convenience init(hexColorString: String) {
        let hexString = hexColorString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        let scanner = Scanner(string: hexString)
        
        if hexString.hasPrefix("#") {
            scanner.scanLocation = 1;
        }
        
        var color: UInt32 = 0
        scanner.scanHexInt32(&color);
        
        let mask = 0x000000FF
        let r = Int(color >> 16) & mask
        let g = Int(color >> 8) & mask
        let b = Int(color) & mask
        
        self.init(red:CGFloat(r), green:CGFloat(g), blue:CGFloat(b))
    }
    
    /// 简便RGB颜色构造器  默认透明度为1
    ///
    /// - Parameters:
    ///   - red: red色值
    ///   - green: green色值
    ///   - blue: blue色值
    convenience init(red: CGFloat, green: CGFloat, blue: CGFloat) {
        self.init(r: red, g: green, b: blue, alpha:1)
    }
    
    /// 简便RGB颜色构造器
    ///
    /// - Parameters:
    ///   - r: red色值
    ///   - g: green色值
    ///   - b: blue色值
    ///   - alpha: 透明度 0~1
    convenience init(r: CGFloat, g: CGFloat, b: CGFloat, alpha: CGFloat) {
        let red = CGFloat(r) / 255.0
        let green = CGFloat(g) / 255.0
        let blue = CGFloat(b) / 255.0
        self.init(red: red, green: green, blue: blue, alpha: alpha)
    }
}
  • NSCode相关
//MARK: NSCoder

/// CGPoint->String
///
/// - Parameter point: point
/// - Returns: 字符串
public func BWStringFromPoint(_ point: CGPoint) -> String {
    return NSCoder.string(for: point)
}

/// CGVector -> String
///
/// - Parameter vector: vector
/// - Returns: 字符串
public func BWStringFromVector(_ vector: CGVector) -> String {
    return NSCoder.string(for: vector)
}

/// CGSize -> String
///
/// - Parameter size: size
/// - Returns: 字符串
public func BWStringFromSize(_ size: CGSize) -> String {
    return NSCoder.string(for: size)
}

/// CGRect -> String
///
/// - Parameter rect: rect
/// - Returns: 字符串
public func BWStringFromRect(_ rect: CGRect) -> String {
    return NSCoder.string(for: rect)
}

/// CGAffineTransform -> String
///
/// - Parameter transform: transform
/// - Returns: 字符串
public func BWStringFromTransform(_ transform: CGAffineTransform) -> String {
    return NSCoder.string(for: transform)
}

/// UIEdgeInsets -> String
///
/// - Parameter insets: insets
/// - Returns: 字符串
public func BWStringFromInsets(_ insets: UIEdgeInsets) -> String {
    return NSCoder.string(for: insets)
}

/// NSDirectionalEdgeInsets -> String
///
/// - Parameter insets: insets
/// - Returns: 字符串
@available(iOS 11.0, *)
public func BWStringFromInsets(_ insets: NSDirectionalEdgeInsets) -> String {
    return NSCoder.string(for: insets)
}

/// UIOffset -> String
///
/// - Parameter offset: offset
/// - Returns: 字符串
public func BWStringFromOffset(_ offset: UIOffset) -> String {
    return NSCoder.string(for: offset)
}

/// String -> CGPoint
///
/// - Parameter string: 字符串
/// - Returns: point
public func BWPointFromString(_ string: String) -> CGPoint {
    return NSCoder.cgPoint(for: string)
}

/// String -> CGVector
/// - Parameter string: 字符串
public func BWVectorFromString(_ string: String) -> CGVector {
    return NSCoder.cgVector(for: string)
}

/// String -> CGSize
/// - Parameter string: 字符串
public func BWSizeFromString(_ string: String) -> CGSize {
    return NSCoder.cgSize(for: string)
}

/// String -> CGRect
/// - Parameter string: 字符串
public func BWRectFromString(_ string: String) -> CGRect {
    return NSCoder.cgRect(for: string)
}

/// String -> CGAffineTransform
/// - Parameter string: 字符串
public func BWTransformFromString(_ string: String) -> CGAffineTransform {
    return NSCoder.cgAffineTransform(for: string)
}

/// String -> UIEdgeInsets
/// - Parameter string: 字符串
public func BWEdgInsetFromString(_ string: String) -> UIEdgeInsets {
    return NSCoder.uiEdgeInsets(for: string)
}

/// String -> NSDirectionalEdgeInsets
/// - Parameter string: 字符串
@available(iOS 11.0, *)
public func BWDirectionalEdgeInsetsFromString(_ string: String) -> NSDirectionalEdgeInsets {
    return NSCoder.nsDirectionalEdgeInsets(for: string)
}

/// String -> UIOffset
/// - Parameter string: 字符串
public func BWOffsetFromString(_ string: String) -> UIOffset {
    return NSCoder.uiOffset(for: string)
}
  • 打印相关
//MARK: DEBUG打印
func BWLog(_ items: Any..., file: String = #file, funcName: String = #function, lineNum: Int = #line) {
    #if DEBUG
    let separator: String = " "
    let terminator: String = "\n"
    let date = Date.init()
    let filenime = (file as NSString).lastPathComponent
    print("\n\(date) \(filenime)[\(lineNum)] method:\(funcName)")
    var i = 0
    let j = items.count
    for item in items {
        i += 1
        // 重点:
        // 当有多个需要打印的参数时,通过三目运算符判断 终止符terminator 此时的实际值
        //   - separator: 分隔符:要在每个项目之间打印的字符串。默认值是单个空格(" ")。
        //   - terminator: 终止符:打印完所有项目后要打印的字符串。默认值是换行符 \n ("\n")。
        print(item, terminator: i == j ? terminator : separator)
    }
    //换行,即打印一个空行.
    print()
    #endif
}

到此为止,以上这些是我最近学习Swift并且使用Swift项目实践的一些小总结,欢迎各位指错
借用昨晚女朋友的一条朋友圈的内容:就像太阳花一样,面朝阳光,竭力生长,保持本色,不骄不躁。希望以后能够更加愉快的学习,愉悦的进步,加油加油加油[KeepFighting][KeepFighting][KeepFighting][KeepFighting]
ps:不经意间的撒了一把狗粮,各位吃饱喝好😃😃😃😃

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容

  • 面对现实需勇气文/游茂星不学无术难提升,找出要点其原因。面对现实需勇气,莫做顽愚一根筋。2018年6月10日 雨灾...
    艳菊疏影阅读 319评论 0 1
  • 11101王世梅 《有个老婆婆吞了一只苍蝇》 绘本导读 解析: 在封面上我们可以得到一些信息,是美国凯迪克奖作品,...
    11001王世梅珠海阅读 1,151评论 1 1
  • 来日并不方长,希望在短暂的生命旅途中,可以做自己想做的事。 今天剪头发的时候在理发店看了一期《笑傲...
    宋宋佳阅读 217评论 2 0
  • 夜色之浓,莫过于黎明前的黑暗 死寂 、诡秘和清寒
    秋大雪阅读 122评论 0 1
  • 不知不觉,已经在家这么多天了,不知道什么时候能开学。 现在还没开学,我就想去学校,可是如果真的要开学了,我想我会舍...
    追梦女孩1阅读 120评论 0 7