不加班小技巧 1 按钮倒计时效果 (属性关联、安全Timer)

先查看效果:
在这里插入图片描述
我们的目的是简单的调用一句代码的方法来完成按钮的倒数计时效果:

@IBAction func btnCodeClicked(_ sender: UIButton) {
	sender.countDown(5)
}

下面我们看看这个方法是怎么实现的:

extension UIButton {
    
    func countDown(_ interval: TimeInterval) {
        info["originalTitle"] = titleLabel?.text
        info["originalColor"] = backgroundColor ?? UIColor()
        tag = Int(interval)
        //set status
        isEnabled = false
        setTitle(String(format: "%d s", tag), for: .normal)
        backgroundColor = .lightGray
        let timer = Timer.safe_scheduledTimerWithTimeInterval(1, closure: {
            var newTitle = ""
            if self.tag <= 0 {
                newTitle = self.info["originalTitle"] as? String ?? ""
                let t = self.info["timer"] as? Timer
                if t != nil {
                    t?.invalidate()
                    self.info["timer"] = nil
                }
                self.isEnabled = true
                self.backgroundColor = self.info["originalColor"] as? UIColor ?? UIColor()
            } else {
                self.tag -= 1
                newTitle = String(format: "%d s", self.tag)
            }
            DispatchQueue.main.async {
                self.setTitle(newTitle, for: .normal)
            }
        }, repeats: true)
        info["timer"] = timer
    }
    
}

随便一个.swift文件里面增加一个extension,这里给UIButton使用了关联属性info,它的类型是[String: Any],后面会给出方法,用它存储相关数据(初始的标题、背景颜色等),用button的tag作为倒计时的时间。在timer中使用了自定义的初始化方法,这个方法可以避免controller引用timer造成的强引用循环,后面给出方法。在timer的action中更新按钮的表现形式。
下面给出关联属性的实现方式:

private var key: Void?

extension NSObject {

    var info: [String: Any] {
        set {
            objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            var obj = objc_getAssociatedObject(self, &key) as? [String: Any]
            if obj?.count ?? 0 <= 0 {
                objc_setAssociatedObject(self, &key, [String: Any](), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                obj = objc_getAssociatedObject(self, &key) as? [String: Any]
            }
            return obj!
        }
    }

}

属性扩展的使用方法我是在extension中增加了计算属性返回NSObject的关联属性,为了让它是一个确定有值的类型,我在get的时候进行了判断空处理,AnyObject的判空是使用.count来判断。如果为空就set并get获得新值返回。
下面给出timer的实现:

// 破除timer引用循环
public typealias TimerExcuteClosure = @convention(block) () -> ()

extension Timer {

    private class TimerActionBlockWrapper : NSObject {
        var block : TimerExcuteClosure
        init(block: @escaping TimerExcuteClosure) {
            self.block = block
        }
    }

    public class func safe_scheduledTimerWithTimeInterval(_ ti:TimeInterval, closure: @escaping TimerExcuteClosure, repeats yesOrNo: Bool) -> Timer {
        return self.scheduledTimer(timeInterval: ti, target: self, selector: #selector(Timer.excuteTimerClosure(_:)), userInfo: TimerActionBlockWrapper(block: closure), repeats: true)
    }

    @objc private class func excuteTimerClosure(_ timer: Timer) {
        if let action = timer.userInfo as? TimerActionBlockWrapper {
            action.block()
        }
    }

}

原理是使用中间类来避免timer的addTarget直接指向timer的引用类

运行效果是点击按钮进入倒数,按钮无法点击,直到时间结束,按钮恢复原来的颜色和标题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值