參照網(wǎng)上的一個(gè)倒計(jì)時(shí)按鈕焦除,以學(xué)習(xí)的目的抄了一下蕉饼,根據(jù)自己的理解修改了一些內(nèi)容,使倒計(jì)時(shí)按鈕用起來(lái)更方便了些帅戒。因?yàn)槭菐讉€(gè)月前寫(xiě)的灯帮,原作者和鏈接也找不著了,記得是在CocoaChina上找到的逻住。這篇文章只為分享和學(xué)習(xí)施流。
1.先說(shuō)思路
思路很簡(jiǎn)單,創(chuàng)建一個(gè)UIButton鄙信,重寫(xiě)構(gòu)造器方法瞪醋,在實(shí)例上添加一個(gè)UIlabel,然后利用計(jì)時(shí)器在label上顯示倒計(jì)時(shí)數(shù)字装诡。這樣做的原因是如果直接設(shè)置button的title數(shù)字切換時(shí)文字會(huì)閃一下才更新银受。然后可以根據(jù)需要給label添加動(dòng)畫(huà)践盼。本篇大部分代碼都是抄的,里面的旋轉(zhuǎn)動(dòng)畫(huà)和放大動(dòng)畫(huà)占了很大的代碼篇幅宾巍,雖然我覺(jué)得兩個(gè)動(dòng)畫(huà)都不太實(shí)用咕幻,也從來(lái)沒(méi)用過(guò)。
2.不多說(shuō)顶霞,貼代碼肄程,邏輯還是很簡(jiǎn)單的
import UIKit
/**
動(dòng)畫(huà)類(lèi)型
*/
enum CountDownAnimationType {
case None //沒(méi)有動(dòng)畫(huà)
case Scale //縮放動(dòng)畫(huà)
case Rotate //旋轉(zhuǎn)動(dòng)畫(huà)
}
/// 自定義倒計(jì)時(shí)按鈕
@IBDesignable class CountDownBtn: UIButton {
/// 倒計(jì)時(shí)中的背景顏色
@IBInspectable var enabled_bgColor: UIColor = UIColor.clearColor()
/// 倒計(jì)時(shí)時(shí)數(shù)字顏色
@IBInspectable var numberColor :UIColor = UIColor.blackColor(){
didSet{
timeLabel.textColor = numberColor
}
}
/// 時(shí)間長(zhǎng)度(秒)
@IBInspectable var count :Int = 0 {
didSet{
startCount = count
originNum = count
}
}
/// 動(dòng)畫(huà)類(lèi)型,默認(rèn)沒(méi)有動(dòng)畫(huà)
var animaType: CountDownAnimationType = CountDownAnimationType.None
override var frame: CGRect {
set{
super.frame = newValue
timeLabel.frame = frame
}
get{
return super.frame
}
}
override var backgroundColor: UIColor?{
set{
super.backgroundColor = newValue
if normalBgColor == nil {
normalBgColor = backgroundColor
}
}
get{
return super.backgroundColor
}
}
private var btnTitle :String?
private var normalBgColor: UIColor?
private var timer: NSTimer!
private var startCount = 0
private var originNum = 0
//倒計(jì)時(shí)Label
private var timeLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
btnTitle = self.titleForState(.Normal)
self.addLabel()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
btnTitle = self.titleForState(.Normal)
self.addLabel()
}
private func addLabel() {
timeLabel.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))
timeLabel.backgroundColor = UIColor.clearColor()
timeLabel.font = UIFont.systemFontOfSize(17)//在開(kāi)啟定時(shí)器時(shí)不排除存儲(chǔ)在設(shè)置字號(hào)失敗的情況
timeLabel.textAlignment = NSTextAlignment.Center
timeLabel.textColor = numberColor
timeLabel.text = ""
self.addSubview(timeLabel)
}
/**
開(kāi)啟倒計(jì)時(shí)
*/
func startCountDown() {
//設(shè)置為按鈕字號(hào)在addLabel()會(huì)失敗
timeLabel.font = self.titleLabel?.font
timeLabel.text = "\(self.originNum)秒"
self.setTitle("", forState: .Normal)
self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(CountDownBtn.countDown), userInfo: nil, repeats: true)
self.backgroundColor = enabled_bgColor
self.enabled = false
switch self.animaType {
case .Scale:
self.numAnimation()
case .Rotate:
self.rotateAnimation()
default:
return
}
}
// 倒計(jì)時(shí)開(kāi)始
@objc private func countDown() {
self.startCount -= 1
timeLabel.text = "\(self.startCount)秒"
//倒計(jì)時(shí)完成后停止定時(shí)器,移除動(dòng)畫(huà)
if self.startCount <= 0 {
if self.timer == nil {
return
}
self.setTitle(btnTitle, forState: .Normal)
timeLabel.layer.removeAllAnimations()
timeLabel.text = ""
self.timer.invalidate()
self.timer = nil
self.enabled = true
self.startCount = self.originNum
self.backgroundColor = normalBgColor
}
}
//放大動(dòng)畫(huà)
private func numAnimation() {
let duration: CFTimeInterval = 1
let beginTime = CACurrentMediaTime()
// Scale animation
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation.keyTimes = [0, 0.5, 1]
scaleAnimation.values = [1, 1.5, 2]
scaleAnimation.duration = duration
// Opacity animation
let opacityAnimaton = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimaton.keyTimes = [0, 0.5, 1]
opacityAnimaton.values = [1, 0.5, 0]
opacityAnimaton.duration = duration
// Animation
let animation = CAAnimationGroup()
animation.animations = [scaleAnimation, opacityAnimaton]
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = duration
animation.repeatCount = HUGE
animation.removedOnCompletion = false
animation.beginTime = beginTime
timeLabel.layer.addAnimation(animation, forKey: "animation")
self.layer.addSublayer(timeLabel.layer)
}
// 旋轉(zhuǎn)變小動(dòng)畫(huà)
private func rotateAnimation() {
let duration: CFTimeInterval = 1
let beginTime = CACurrentMediaTime()
// Rotate animation
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.fromValue = NSNumber(int: 0)
rotateAnimation.toValue = NSNumber(double: M_PI * 2)
rotateAnimation.duration = duration;
// Scale animation
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation.keyTimes = [0]
scaleAnimation.values = [1, 2]
scaleAnimation.duration = 0
// Opacity animation
let opacityAnimaton = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimaton.keyTimes = [0, 0.5]
opacityAnimaton.values = [1, 0]
opacityAnimaton.duration = duration
// Scale animation
let scaleAnimation2 = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation2.keyTimes = [0, 0.5]
scaleAnimation2.values = [2, 0]
scaleAnimation2.duration = duration
let animation = CAAnimationGroup()
animation.animations = [rotateAnimation, scaleAnimation, opacityAnimaton, scaleAnimation2]
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = duration
animation.repeatCount = HUGE
animation.removedOnCompletion = false
animation.beginTime = beginTime
timeLabel.layer.addAnimation(animation, forKey: "animation")
self.layer.addSublayer(timeLabel.layer)
}
}
這里簡(jiǎn)單說(shuō)下@IBDesignable和@IBInspectable选浑,@IBDesignable標(biāo)記類(lèi)蓝厌,@IBInspectable標(biāo)記屬性,可以使這個(gè)類(lèi)的屬性在xib和storyboard設(shè)置面板中進(jìn)行設(shè)置古徒,配合didSet可以直接在xib和storyboard面板中顯示實(shí)時(shí)效果拓提。
3.使用
這里只展示下在storyBoard里的用法
a.把代碼放入工程中。
b.拖一個(gè)UIButton隧膘,修改父類(lèi)為CountDownBtn代态。
c.設(shè)置相關(guān)屬性
按鈕的默認(rèn)標(biāo)題,字號(hào),普通狀態(tài)下按鈕文字顏色是在Button里設(shè)置的,頂部三個(gè)屬性分別為倒計(jì)時(shí)狀態(tài)下按鈕背景色疹吃,倒計(jì)時(shí)狀態(tài)下文字顏色蹦疑,倒計(jì)時(shí)的時(shí)間設(shè)置。
d.點(diǎn)擊事件
@IBAction func getCodeAction(sender: CountDownBtn) {
// 設(shè)置動(dòng)畫(huà)類(lèi)型,可以不寫(xiě),默認(rèn)沒(méi)有動(dòng)畫(huà)效果
sender.animaType = .Rotate
// 開(kāi)啟倒計(jì)時(shí)
sender.startCountDown()
// 這里寫(xiě)其它操作萨驶,如獲取驗(yàn)證碼
}
這里注意sender的類(lèi)型歉摧,或者自己進(jìn)行轉(zhuǎn)換。
最后
整個(gè)代碼邏輯很簡(jiǎn)單篡撵,可以根據(jù)需要定制下代碼判莉。另外里面的邏輯也可以通過(guò)OC實(shí)現(xiàn)豆挽,didSet方法應(yīng)該可以用KVO代替,或者可以使用set方法(我也不確定)育谬。另外,@IBDesignable和@IBInspectable在OC上的替代方式如下:
IB_DESIGNABLE
@interface CustomButton : UIButton
@property (nonatomic, strong) IBInspectable UIImage *highlightSelectedImage;
@end
如果有誰(shuí)用OC寫(xiě)了請(qǐng)分享我一份??。
有什么地方做的不好帮哈,歡迎指出膛檀。相互學(xué)習(xí)才能更快進(jìn)步。
Demo
Swift4.0更新娘侍,沒(méi)上傳咖刃,自用
import UIKit
/**
動(dòng)畫(huà)類(lèi)型
*/
enum CountDownAnimationType {
case None //沒(méi)有動(dòng)畫(huà)
case Scale //縮放動(dòng)畫(huà)
case Rotate //旋轉(zhuǎn)動(dòng)畫(huà)
}
/// 自定義倒計(jì)時(shí)按鈕
@IBDesignable class CountDownBtn: UIButton {
/// 倒計(jì)時(shí)中的背景顏色
@IBInspectable var enabled_bgColor: UIColor = UIColor.clear
/// 倒計(jì)時(shí)時(shí)數(shù)字顏色
@IBInspectable var numberColor :UIColor = UIColor.black{
didSet{
timeLabel.textColor = numberColor
}
}
/// 時(shí)間長(zhǎng)度(秒)
@IBInspectable var count :Int = 0 {
didSet{
startCount = count
originNum = count
}
}
/// 動(dòng)畫(huà)類(lèi)型,默認(rèn)沒(méi)有動(dòng)畫(huà)
var animaType: CountDownAnimationType = CountDownAnimationType.None
override var frame: CGRect {
set{
super.frame = newValue
timeLabel.frame = frame
}
get{
return super.frame
}
}
override var backgroundColor: UIColor?{
set{
super.backgroundColor = newValue
if normalBgColor == nil {
normalBgColor = backgroundColor
}
}
get{
return super.backgroundColor
}
}
private var btnTitle :String?
private var normalBgColor: UIColor?
private var timer: Timer!
private var startCount = 0
private var originNum = 0
//倒計(jì)時(shí)Label
private var timeLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
btnTitle = self.title(for: .normal)
self.addLabel()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
btnTitle = self.title(for: .normal)
self.addLabel()
}
private func addLabel() {
timeLabel.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
//CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))
timeLabel.backgroundColor = UIColor.clear
timeLabel.font = UIFont.systemFont(ofSize: 17)//在開(kāi)啟定時(shí)器時(shí)不排除存儲(chǔ)在設(shè)置字號(hào)失敗的情況
timeLabel.textAlignment = NSTextAlignment.center
timeLabel.textColor = numberColor
timeLabel.text = ""
self.addSubview(timeLabel)
}
/**
開(kāi)啟倒計(jì)時(shí)
*/
func startCountDown() {
//設(shè)置為按鈕字號(hào)在addLabel()會(huì)失敗
timeLabel.font = self.titleLabel?.font
timeLabel.text = "\(self.originNum)秒"
self.setTitle("", for: .normal)
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(CountDownBtn.countDown), userInfo: nil, repeats: true)
self.backgroundColor = enabled_bgColor
self.isEnabled = false
switch self.animaType {
case .Scale:
self.numAnimation()
case .Rotate:
self.rotateAnimation()
default:
return
}
}
// 倒計(jì)時(shí)開(kāi)始
@objc private func countDown() {
self.startCount -= 1
timeLabel.text = "\(self.startCount)秒"
//倒計(jì)時(shí)完成后停止定時(shí)器,移除動(dòng)畫(huà)
if self.startCount <= 0 {
if self.timer == nil {
return
}
self.setTitle(btnTitle, for: .normal)
timeLabel.layer.removeAllAnimations()
timeLabel.text = ""
self.timer.invalidate()
self.timer = nil
self.isEnabled = true
self.startCount = self.originNum
self.backgroundColor = normalBgColor
}
}
//放大動(dòng)畫(huà)
private func numAnimation() {
let duration: CFTimeInterval = 1
let beginTime = CACurrentMediaTime()
// Scale animation
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation.keyTimes = [0, 0.5, 1]
scaleAnimation.values = [1, 1.5, 2]
scaleAnimation.duration = duration
// Opacity animation
let opacityAnimaton = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimaton.keyTimes = [0, 0.5, 1]
opacityAnimaton.values = [1, 0.5, 0]
opacityAnimaton.duration = duration
// Animation
let animation = CAAnimationGroup()
animation.animations = [scaleAnimation, opacityAnimaton]
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = duration
animation.repeatCount = HUGE
animation.isRemovedOnCompletion = false
animation.beginTime = beginTime
timeLabel.layer.add(animation, forKey: "animation")
self.layer.addSublayer(timeLabel.layer)
}
// 旋轉(zhuǎn)變小動(dòng)畫(huà)
private func rotateAnimation() {
let duration: CFTimeInterval = 1
let beginTime = CACurrentMediaTime()
// Rotate animation
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.fromValue = NSNumber(value: 0)
rotateAnimation.toValue = NSNumber(value: Double.pi * 2)
rotateAnimation.duration = duration;
// Scale animation
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation.keyTimes = [0]
scaleAnimation.values = [1, 2]
scaleAnimation.duration = 0
// Opacity animation
let opacityAnimaton = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimaton.keyTimes = [0, 0.5]
opacityAnimaton.values = [1, 0]
opacityAnimaton.duration = duration
// Scale animation
let scaleAnimation2 = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation2.keyTimes = [0, 0.5]
scaleAnimation2.values = [2, 0]
scaleAnimation2.duration = duration
let animation = CAAnimationGroup()
animation.animations = [rotateAnimation, scaleAnimation, opacityAnimaton, scaleAnimation2]
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = duration
animation.repeatCount = HUGE
animation.isRemovedOnCompletion = false
animation.beginTime = beginTime
timeLabel.layer.add(animation, forKey: "animation")
self.layer.addSublayer(timeLabel.layer)
}
}