JKSwiftExtension栗精,測試用例在 UIButtonExtensionViewController.swift 里面
目錄:
1、基本的擴展
2瞻鹏、鏈式調(diào)用
3悲立、UIButton 圖片 與 title 位置關(guān)系(提示:title和image要在設(shè)置布局關(guān)系之前設(shè)置)
4、自帶倒計時功能的 Button
一新博、基本的擴展
// MARK:- 一薪夕、基本的擴展
public extension UIButton {
enum SmallButtonType {
case red
case pink
}
// MARK: 1.1、創(chuàng)建一個帶顏色的 Button
/// 創(chuàng)建一個帶顏色的 Button
/// - Parameters:
/// - type: 類型
/// - height: 高度
/// - Returns: 返回自身
@discardableResult
static func small(type: SmallButtonType = .red, height: CGFloat = 45) -> UIButton {
let normalColor: UIColor
let disabledColor: UIColor
let lineTypeNormal: LineType
let lineTypeDisable: LineType
let titleColorNormal: UIColor
let titleColorDisable: UIColor
switch type {
case .red:
normalColor = .hexStringColor(hexString: "#E54749")
disabledColor = .hexStringColor(hexString: "#CCCCCC")
lineTypeNormal = .none
lineTypeDisable = .none
titleColorNormal = .white
titleColorDisable = .white
case .pink:
normalColor = .hexStringColor(hexString: "#FFE8E8")
disabledColor = .hexStringColor(hexString: "#CCCCCC")
lineTypeNormal = .color(.hexStringColor(hexString: "#F6CDCD"))
lineTypeDisable = .color(.hexStringColor(hexString: "#9C9C9C"))
titleColorNormal = .hexStringColor(hexString: "#E54749")
titleColorDisable = .white
}
let btn = UIButton(type: .custom).font(.systemFont(ofSize: 16))
btn.setTitleColor(titleColorNormal, for: .normal)
btn.setTitleColor(titleColorDisable, for: .disabled)
btn.setBackgroundImage(drawSmallBtn(color: normalColor, height: height, lineType: lineTypeNormal), for: .normal)
btn.setBackgroundImage(drawSmallBtn(color: disabledColor, height: height, lineType: lineTypeDisable), for: .disabled)
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 13, right: 0)
return btn
}
// MARK: 1.2赫悄、創(chuàng)建一個常規(guī)的 Button
/// 創(chuàng)建一個常規(guī)的 Button
/// - Returns: 返回自身
static func normal() -> UIButton {
let btn = UIButton(type: .custom).font(.boldSystemFont(ofSize: 18))
btn.setTitleColor(.white, for: .normal)
btn.setTitleColor(.white, for: .disabled)
btn.setBackgroundImage(drawNormalBtn(color: .hexStringColor(hexString: "#E54749"))?.resizableImage(withCapInsets: UIEdgeInsets(top: 10, left: 15, bottom: 15, right: 15)), for: .normal)
btn.setBackgroundImage(drawNormalBtn(color: .hexStringColor(hexString: "#CCCCCC"))?.resizableImage(withCapInsets: UIEdgeInsets(top: 10, left: 15, bottom: 15, right: 15)), for: .disabled)
btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 4, right: 0)
return btn
}
private static func drawSmallBtn(color: UIColor, height: CGFloat, lineType: LineType) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: 200, height: height + 20)
let path = UIBezierPath(roundedRect: CGRect(x: 10, y: 3, width: 180, height: height), cornerRadius: height / 2)
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.addPath(path.cgPath)
context?.setShadow(offset: CGSize(width: 1, height: 4), blur: 10, color: color.withAlphaComponent(0.5).cgColor)
context?.fillPath()
switch lineType {
case let .color(color):
color.setStroke()
path.lineWidth = kPixel
path.stroke()
default:
break
}
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
private static func drawNormalBtn(color: UIColor) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: 260, height: 50)
let path = UIBezierPath(roundedRect: CGRect(x: 10, y: 3, width: 240, height: 40), cornerRadius: 3)
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.addPath(path.cgPath)
context?.setShadow(offset: CGSize(width: 1, height: 2), blur: 6, color: color.withAlphaComponent(0.5).cgColor)
context?.fillPath()
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
}
二原献、鏈式調(diào)用
// MARK:- 二、鏈式調(diào)用
public extension UIButton {
// MARK: 2.1埂淮、設(shè)置title
/// 設(shè)置title
/// - Parameters:
/// - text: 文字
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func title(_ text: String, _ state: UIControl.State = .normal) -> Self {
setTitle(text, for: state)
return self
}
// MARK: 2.2姑隅、設(shè)置文字顏色
/// 設(shè)置文字顏色
/// - Parameters:
/// - color: 文字顏色
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func textColor(_ color: UIColor, _ state: UIControl.State = .normal) -> Self {
setTitleColor(color, for: state)
return self
}
// MARK: 2.3、設(shè)置字體大小(UIFont)
/// 設(shè)置字體大小
/// - Parameter font: 字體 UIFont
/// - Returns: 返回自身
@discardableResult
func font(_ font: UIFont) -> Self {
titleLabel?.font = font
return self
}
// MARK: 2.4倔撞、設(shè)置字體大小(CGFloat)
/// 設(shè)置字體大小(CGFloat)
/// - Parameter fontSize: 字體的大小
/// - Returns: 返回自身
@discardableResult
func font(_ fontSize: CGFloat) -> Self {
titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
return self
}
// MARK: 2.5讲仰、設(shè)置字體粗體
/// 設(shè)置粗體
/// - Parameter fontSize: 設(shè)置字體粗體
/// - Returns: 返回自身
@discardableResult
func boldFont(_ fontSize: CGFloat) -> Self {
titleLabel?.font = UIFont.boldSystemFont(ofSize: fontSize)
return self
}
// MARK: 2.6、設(shè)置圖片
/// 設(shè)置圖片
/// - Parameters:
/// - image: 圖片
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func image(_ image: UIImage?, _ state: UIControl.State = .normal) -> Self {
setImage(image, for: state)
return self
}
// MARK: 2.7痪蝇、設(shè)置圖片(通過Bundle加載)
/// 設(shè)置圖片(通過Bundle加載)
/// - Parameters:
/// - bundle: Bundle
/// - imageName: 圖片名字
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func image(in bundle: Bundle? = nil, _ imageName: String, _ state: UIControl.State = .normal) -> Self {
let image = UIImage(named: imageName, in: bundle, compatibleWith: nil)
setImage(image, for: state)
return self
}
// MARK: 2.8鄙陡、設(shè)置圖片(通過Bundle加載)
/// 設(shè)置圖片(通過Bundle加載)
/// - Parameters:
/// - aClass: className bundle所在的類的類名
/// - bundleName: bundle 的名字
/// - imageName: 圖片的名字
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func image(forParent aClass: AnyClass, bundleName: String, _ imageName: String, _ state: UIControl.State = .normal) -> Self {
guard let path = Bundle(for: aClass).path(forResource: bundleName, ofType: "bundle") else {
return self
}
let image = UIImage(named: imageName, in: Bundle(path: path), compatibleWith: nil)
setImage(image, for: state)
return self
}
// MARK: 2.9冕房、設(shè)置圖片(純顏色的圖片)
/// 設(shè)置圖片(純顏色的圖片)
/// - Parameters:
/// - color: 圖片顏色
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func image(_ color: UIColor, _ size: CGSize = CGSize(width: 20.0, height: 20.0), _ state: UIControl.State = .normal) -> Self {
let image = UIImage.image(color: color, size: size)
setImage(image, for: state)
return self
}
// MARK: 2.10、設(shè)置背景圖片
/// 設(shè)置背景圖片
/// - Parameters:
/// - image: 圖片
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func bgImage(_ image: UIImage?, _ state: UIControl.State = .normal) -> Self {
setBackgroundImage(image, for: state)
return self
}
// MARK: 2.11趁矾、設(shè)置背景圖片(通過Bundle加載)
/// 設(shè)置背景圖片(通過Bundle加載)
/// - Parameters:
/// - aClass: className bundle所在的類的類名
/// - bundleName: bundle 的名字
/// - imageName: 圖片的名字
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func bgImage(forParent aClass: AnyClass, bundleName: String, _ imageName: String, _: UIControl.State = .normal) -> Self {
guard let path = Bundle(for: aClass).path(forResource: bundleName, ofType: "bundle") else {
return self
}
let image = UIImage(named: imageName, in: Bundle(path: path), compatibleWith: nil)
setBackgroundImage(image, for: state)
return self
}
// MARK: 2.12毒费、設(shè)置背景圖片(通過Bundle加載)
/// 設(shè)置背景圖片(通過Bundle加載)
/// - Parameters:
/// - bundle: Bundle
/// - imageName: 圖片的名字
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func bgImage(in bundle: Bundle? = nil, _ imageName: String, _ state: UIControl.State = .normal) -> Self {
let image = UIImage(named: imageName, in: bundle, compatibleWith: nil)
setBackgroundImage(image, for: state)
return self
}
// MARK: 2.13、設(shè)置背景圖片(純顏色的圖片)
/// 設(shè)置背景圖片(純顏色的圖片)
/// - Parameters:
/// - color: 背景色
/// - state: 狀態(tài)
/// - Returns: 返回自身
@discardableResult
func bgImage(_ color: UIColor, _ state: UIControl.State = .normal) -> Self {
let image = UIImage.image(color: color)
setBackgroundImage(image, for: state)
return self
}
// MARK: 2.14愈魏、按鈕點擊的變化
/// 按鈕點擊的變化
/// - Returns: 返回自身
@discardableResult
func confirmButton() -> Self {
let normalImage = UIImage.image(color: UIColor.hexStringColor(hexString: "#E54749"), size: CGSize(width: 10, height: 10), round: 4)?.resizableImage(withCapInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
let disableImg = UIImage.image(color: UIColor.hexStringColor(hexString: "#E6E6E6"), size: CGSize(width: 10, height: 10), round: 4)?.resizableImage(withCapInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
setBackgroundImage(normalImage, for: .normal)
setBackgroundImage(disableImg, for: .disabled)
return self
}
}
三、UIButton 圖片 與 title 位置關(guān)系(提示:title和image要在設(shè)置布局關(guān)系之前設(shè)置)
// MARK:- 三想际、UIButton 圖片 與 title 位置關(guān)系
/// UIButton 圖片與title位置關(guān)系 http://www.reibang.com/p/0f34c1b52604
public extension UIButton {
/// 圖片 和 title 的布局樣式
enum ImageTitleLayout {
case imgTop
case imgBottom
case imgLeft
case imgRight
}
// MARK: 3.1培漏、設(shè)置圖片和 title 的位置關(guān)系(提示:title和image要在設(shè)置布局關(guān)系之前設(shè)置)
/// 設(shè)置圖片和 title 的位置關(guān)系(提示:title和image要在設(shè)置布局關(guān)系之前設(shè)置)
/// - Parameters:
/// - layout: 布局
/// - spacing: 間距
/// - Returns: 返回自身
@discardableResult
func setImageTitleLayout(_ layout: ImageTitleLayout, spacing: CGFloat = 0) -> Self {
switch layout {
case .imgLeft:
alignHorizontal(spacing: spacing, imageFirst: true)
case .imgRight:
alignHorizontal(spacing: spacing, imageFirst: false)
case .imgTop:
alignVertical(spacing: spacing, imageTop: true)
case .imgBottom:
alignVertical(spacing: spacing, imageTop: false)
}
return self
}
/// 水平方向
/// - Parameters:
/// - spacing: 間距
/// - imageFirst: 圖片是否優(yōu)先
private func alignHorizontal(spacing: CGFloat, imageFirst: Bool) {
let edgeOffset = spacing / 2
imageEdgeInsets = UIEdgeInsets(top: 0,
left: -edgeOffset,
bottom: 0,
right: edgeOffset)
titleEdgeInsets = UIEdgeInsets(top: 0,
left: edgeOffset,
bottom: 0,
right: -edgeOffset)
if !imageFirst {
self.transform = CGAffineTransform(scaleX: -1, y: 1)
imageView?.transform = CGAffineTransform(scaleX: -1, y: 1)
titleLabel?.transform = CGAffineTransform(scaleX: -1, y: 1)
}
contentEdgeInsets = UIEdgeInsets(top: 0, left: edgeOffset, bottom: 0, right: edgeOffset)
}
/// 垂直方向
/// - Parameters:
/// - spacing: 間距
/// - imageTop: 圖片是不是在頂部
private func alignVertical(spacing: CGFloat, imageTop: Bool) {
guard let imageSize = self.imageView?.image?.size,
let text = self.titleLabel?.text,
let font = self.titleLabel?.font
else {
return
}
let labelString = NSString(string: text)
let titleSize = labelString.size(withAttributes: [NSAttributedString.Key.font: font])
let imageVerticalOffset = (titleSize.height + spacing) / 2
let titleVerticalOffset = (imageSize.height + spacing) / 2
let imageHorizontalOffset = (titleSize.width) / 2
let titleHorizontalOffset = (imageSize.width) / 2
let sign: CGFloat = imageTop ? 1 : -1
imageEdgeInsets = UIEdgeInsets(top: -imageVerticalOffset * sign,
left: imageHorizontalOffset,
bottom: imageVerticalOffset * sign,
right: -imageHorizontalOffset)
titleEdgeInsets = UIEdgeInsets(top: titleVerticalOffset * sign,
left: -titleHorizontalOffset,
bottom: -titleVerticalOffset * sign,
right: titleHorizontalOffset)
// increase content height to avoid clipping
let edgeOffset = (min(imageSize.height, titleSize.height) + spacing)/2
contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0, bottom: edgeOffset, right: 0)
}
}
四、自帶倒計時功能的 Button"
// MARK:- 四胡本、自帶倒計時功能的 Button(有待改進)
/// 自帶倒計時功能的Button
/// - 狀態(tài)分為 [倒計時中牌柄,倒計時完成],分別提供回調(diào)
/// - 需要和業(yè)務(wù)結(jié)合時侧甫,后期再考慮
public extension UIButton {
// MARK: 4.1珊佣、設(shè)置 Button 倒計時
/// 設(shè)置 Button 倒計時
/// - Parameters:
/// - count: 最初的倒計時數(shù)字
/// - timering: 倒計時中的 Block
/// - complete: 倒計時完成的 Block
/// - timeringPrefix: 倒計時文字的:前綴
/// - completeText: 倒計時完成后的文字
func countDown(_ count: Int, timering: TimeringBlock? = nil, complete: CompletionBlock? = nil, timeringPrefix: String = "再次獲取", completeText: String = "重新獲取") {
isEnabled = false
let begin = ProcessInfo().systemUptime
let c_default = UIColor.hexStringColor(hexString: "#2798fd")
let c_default_disable = UIColor.hexStringColor(hexString: "#999999")
self.textColor(titleColor(for: .normal) ?? c_default)
self.textColor(titleColor(for: .disabled) ?? c_default_disable, .disabled)
var remainingCount: Int = count {
willSet {
setTitle(timeringPrefix + "(\(newValue)s)", for: .normal)
if newValue <= 0 {
setTitle(completeText, for: .normal)
}
}
}
self.invalidate()
self.timer = DispatchSource.makeTimerSource(queue:DispatchQueue.global())
self.timer?.schedule(deadline: .now(), repeating: .seconds(1))
self.isTiming = true
self.timer?.setEventHandler(handler: {
DispatchQueue.main.async {
remainingCount = count - Int(ProcessInfo().systemUptime - begin)
if remainingCount <= 0 {
if let cb = complete {
cb()
}
// 計時結(jié)束后,enable的條件
self.isEnabled = self.reEnableCond ?? true
self.isTiming = false
self.invalidate()
} else {
if let tb = timering {
tb(remainingCount)
}
}
}
})
self.timer?.resume()
}
// MARK: 4.2披粟、是否可以點擊
/// 是否可以點擊
var reEnableCond: Bool? {
get {
if let value = objc_getAssociatedObject(self, &TimerKey.reEnableCond_key) {
return value as? Bool
}
return nil
}
set {
objc_setAssociatedObject(self, &TimerKey.reEnableCond_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// MARK: 4.3咒锻、是否正在倒計時
/// 是否正在倒計時
var isTiming: Bool {
get {
if let value = objc_getAssociatedObject(self, &TimerKey.running_key) {
return value as! Bool
}
// 默認狀態(tài)
return false
}
set {
objc_setAssociatedObject(self, &TimerKey.running_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// MARK: 4.3、處于倒計時時守屉,前綴文案惑艇,如:「再次獲取」 + (xxxs)
/// 處于倒計時時,前綴文案拇泛,如:「再次獲取」 + (xxxs)
var timeringPrefix: String? {
get {
if let value = objc_getAssociatedObject(self, &TimerKey.timeringPrefix_key) {
return value as? String
}
return nil
}
set {
objc_setAssociatedObject(self, &TimerKey.timeringPrefix_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// MARK: 銷毀定時器
/// 銷毀定時器
func invalidate() {
if self.timer != nil {
self.timer?.cancel()
self.timer = nil
}
}
// MARK: 時間對象
/// 時間對象
var timer: DispatchSourceTimer? {
get {
if let value = objc_getAssociatedObject(self, &TimerKey.timer_key) {
return value as? DispatchSourceTimer
}
return nil
}
set {
objc_setAssociatedObject(self, &TimerKey.timer_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
typealias TimeringBlock = (Int) -> ()
typealias CompletionBlock = () -> ()
private struct TimerKey {
static var timer_key = "timer_key"
static var running_key = "running_key"
static var timeringPrefix_key = "timering_prefix_key"
static var reEnableCond_key = "re_enable_cond_key"
}
}