最近整理了一些我自己在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:不经意间的撒了一把狗粮,各位吃饱喝好😃😃😃😃