30分钟倒计时 swift_swift 倒计时 - CSDN
  • 倒计时控件,包括按钮倒计时,label倒计时以及在cell中的倒计时
  • Swift3.0 GCD定时器实现倒计时,UIDatePicker的使用, 仿写一个活动倒计时的DEMO

    关键代码

    //截止日期
    let endDate = datePicker.date
    //开始日期
    let startDate = Date()
    //时间间隔
    let timeInterval:TimeInterval = endDate.timeIntervalSince(startDate)
    
    if timer == nil {
        //剩余时间
        var timeout = timeInterval
        if timeout != 0 {
    
            //创建全局队列
            let queue = DispatchQueue.global()
            //在全局队列下创建一个时间源
            timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
            //设定循环的间隔是一秒,并且立即开始
            timer?.scheduleRepeating(wallDeadline: DispatchWallTime.now(), interval: .seconds(1))
            //时间源出发事件
            timer?.setEventHandler(handler: {
                //必须是当前日期往后的日期,在datePicker上也做了限制
                if timeout <= 0 {
                    self.timer?.cancel()
                    self.timer = nil
                    DispatchQueue.main.async(execute: {
                        self.day.text = "00"
                        self.hour.text = "00"
                        self.minute.text = "00"
                        self.second.text = "00"
                    })
                } else {
                    //计算剩余时间
                    let days = Int(timeout) / (3600 * 24)
                    if days == 0 {
                        self.day.text = ""
                    }
                    let hours = (Int(timeout) - Int(days) * 24 * 3600) / 3600
                    let minutes = (Int(timeout) - Int(days) * 24 * 3600 - Int(hours) * 3600) / 60
                    let seconds = Int(timeout) - Int(days) * 24 * 3600 - Int(hours) * 3600 - Int(minutes) * 60
                    //主队列中刷新UI
                    DispatchQueue.main.async(execute: {
                        if days == 0 {
                            self.day.text = "0"
                        } else {
                            self.day.text = "\(days)"
                        }
                        if hours < 10 {
                            self.hour.text = "0" + "\(hours)"
                        } else {
                            self.hour.text = "\(hours)"
                        }
                        if minutes < 10 {
                            self.minute.text = "0" + "\(minutes)"
                        } else {
                            self.minute.text = "\(minutes)"
                        }
                        if seconds < 10 {
                            self.second.text = "0" + "\(seconds)"
                        } else {
                            self.second.text = "\(seconds)"
                        }
                    })
                    timeout -= 1
                }
            })
            //启动时间源
            timer?.resume()
        }
    }

    DEMO效果图

    这里写图片描述

    DEMO下载地址

    展开全文
  • 今天遇到倒计时问题,需要后台也一直计时。 我思考了一下,总计了两种思路。 第一个思路是通过苹果自带的后台任务进行倒计时。 第二种思路是记录当前进入后台的时间,以及再次进入前台的时间。计算两者的时间间隔...

    今天遇到倒计时问题,需要后台也一直计时。

    我思考了一下,总计了两种思路。

    第一个思路是通过苹果自带的后台任务进行倒计时。

    第二种思路是记录当前进入后台的时间,以及再次进入前台的时间。计算两者的时间间隔,然后通过进入后台时刻的秒数减去这个时间间隔。就是真实的倒计时。

    具体代码如下:

    希望能帮到需要的人,也给自己记录一下。

    展开全文
  • 一个更完善的Swift倒计时按钮(附后台权限申请) 如今越来越多app使用手机号码作为用户名,其中总是要涉及到验证码的发送。 倒计时按钮实现关键点: 当前视图控制器销毁后倒计时计数的再恢复 不同页面可能使用同一...

    一个更完善的Swift倒计时按钮(附后台权限申请)

    如今越来越多app使用手机号码作为用户名,其中总是要涉及到验证码的发送。

    倒计时按钮实现关键点:

    1. 当前视图控制器销毁后倒计时计数的再恢复
    2. 不同页面可能使用同一个倒计时计数
    3. app进入后台后的倒计时

    明确了上面几个问题,接下来代码编写就简单了

    符合设计模式,我们将倒计时按钮实现拆分为两个类:

    WynCountdownButton: 继承UIButton,对外开放

    WynCountdownController: 倒计时控制模块,与WynCountdownButton一一对应。对外不可见

    下面针对3个实现关键点做实现设计

    1. 当前视图控制器销毁后倒计时计数的再恢复

    • 按钮控制器分离。按钮生命周期随所处的视图控制器。控制器随倒计时的开始与结束,做初始化与销毁。
    • 按钮控制器一一对应
    class WynCountdownButton: UIButton {
    
    	private weak var controller: WynCountdownController
    	
    	...
    }
    
    /// 全局的常量或变量都是延迟计算的,跟延迟存储属性相似,但全局的常量或变量不需要标记‘lazy’特性。
    /// 全局变量持有控制器
    private var wynCountdownControllers: [String: WynCountdownController] = [:]
    
    

    2.不同页面可能使用同一个倒计时计数

    class WynCountdownController {
    	/// 通过identifier来取得控制器实例
       static func shared(withIdentifier identifier: String) -> WynCountdownController {
    
            if let c = wynCountdownControllers[identifier] {
                return c
            } else {
                let c = WynCountdownController()
                c.identifier = identifier
                objc_sync_enter(wynCountdownControllers)
                wynCountdownControllers[identifier] = c
                objc_sync_exit(wynCountdownControllers)
    
                return c
            }
        }
        /// 限制只能通过shared(withIdentifier:)方法来实例化Controller
        private init() {}
    
    	...
    }
    
    

    3. app进入后台后的倒计时

    这点就和我们的倒计时按钮没有关系了。有两种实现方案

    1. 记录进入后台与回到前台的间隔时间
    2. 申请后台运行权限

    本文介绍一下2,申请后台运行权限的方法。 只需在AppDelegate中添加以下代码

    import UIKit
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    	...
    	
    	var backgroundTaskId: UIBackgroundTaskIdentifier?
        func applicationDidEnterBackground(_ application: UIApplication) {
    
                let sharedApp = UIApplication.shared
                backgroundTaskId = sharedApp.beginBackgroundTask(expirationHandler: { [unowned sharedApp] in
    
                    sharedApp.endBackgroundTask(self.backgroundTaskId!)
                    self.backgroundTaskId = UIBackgroundTaskIdentifier.invalid
                })
    	}
    	
    	...
    }
    
    

    若采用此方案,推荐加入一个标识,判断当前是否需要申请后台运行权

    下面是完整代码,暂未传到Github

    使用方法

    let cBtn = WynCountdownButton(sec: 30, type: .system, identifier: "HistoryAndFavoriteBtn")
    cBtn.frame = CGRect(x: 100, y: 250, width: 120, height: 44)
    cBtn.setTitle("点击获取验证码", for: .normal)
    cBtn.attributedTitleForCountingClosure = { (btn, sec) in
    	return NSAttributedString(string: "剩余\(sec)秒", attributes:[.foregroundColor: UIColor.random()])
    }
    cBtn.didCountdownBeginClosure = { (btn) in
    	kIsBgTaskEnable = true
    }
    cBtn.didCountdownFinishClosure = { (btn) in
    	print("Finished")
    	kIsBgTaskEnable = false
    }
    
    	view.addSubview(cBtn)
    
    

    WynCountdownButton.swift

    import UIKit
    
    class WynCountdownButton: UIButton {
    
        /// 必须设置
        @IBInspectable
        public var identifier: String! {
            didSet {
                controller = WynCountdownController.shared(withIdentifier: identifier)
            }
        }
        /// 倒计时长
        @IBInspectable
        public var sec: Int = 60
    
        /// 倒数时按钮显示文字的 前缀
        @IBInspectable
        public var countingPrefix: String = ""
        /// 倒数时按钮显示文字的 后缀
        @IBInspectable
        public var countingSuffix: String = ""
    
        /// 自定义倒计时时显示的文字(每秒回调一次,closure设定后,前后缀的设定无效化)
        public var titleForCountingClosure: ((UIButton, Int) -> String)?
        public var attributedTitleForCountingClosure: ((UIButton, Int) -> NSAttributedString)?
    
    
        /// 倒计时开始、结束回调
        public var didCountdownBeginClosure: ((UIButton) -> Void)?
        public var didCountdownFinishClosure: ((UIButton) -> Void)?
    
        /* ============================================================ */
        // MARK: - Initilize
        /* ============================================================ */
    
        /// 初始化倒计时按钮
        ///
        /// - Parameters:
        ///   - sec: 倒计时长(秒)
        ///   - identifier: 唯一标示,用于恢复倒计时
        ///   - type: 按钮类型
        convenience init(sec: Int, type: ButtonType, identifier: String = "identifier") {
            self.init(type: type)
    
            self.sec = sec
            self.identifier = identifier
            self.controller = WynCountdownController.shared(withIdentifier: identifier)
            if controller.isTicking {
                self.start()
            }
        }
    
        /* ============================================================ */
        // MARK: - Public func
        /* ============================================================ */
    
        /// 开始倒计时
        @objc public func start() {
    
            isEnabled = false
    
            let tickingHandler: ((Timer, Int) -> Void)  = { [weak self](timer, currentVal) in
                guard let strongSelf = self else { return }
    
                strongSelf.handleTicking(currentVal: currentVal)
            }
    
            if controller.isTicking {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    // 延时以等待设置TitleForCountingClosure
                    self.handleTicking(currentVal: self.controller.currentVal)
                    self.controller.tick = tickingHandler
                }
            } else {
                controller.begin(sec: sec, tickClosure: tickingHandler)
            }
    
            didCountdownBeginClosure?(self)
        }
    
        /* ============================================================ */
        // MARK: - Private properties & function
        /* ============================================================ */
        private weak var controller: WynCountdownController! {
            didSet {
                if controller.isTicking {
                    self.start()
                }
            }
        }
    
        private func handleTicking(currentVal: Int) {
    
            if currentVal > 0 {
                if let closure = titleForCountingClosure {
    
                    setTitle(closure(self, currentVal), for: .disabled)
                    return
                }
    
                if let closure = attributedTitleForCountingClosure {
    
                    setAttributedTitle(closure(self, currentVal), for: .disabled)
                    return
                }
    
                setTitle("\(countingPrefix)\(currentVal)\(countingSuffix)", for: .disabled)
    
            } else {
                didCountdownFinishClosure?(self)
                isEnabled = true
            }
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            self.addTarget(self, action: #selector(start), for: .touchUpInside)
        }
    
        /// IB方式创建
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            self.addTarget(self, action: #selector(start), for: .touchUpInside)
    
            /// 检查未填写identifier
            if identifier != nil && identifier != "" {
                self.controller = WynCountdownController.shared(withIdentifier: identifier)
            }
        }
    }
    
    

    WynCountdownController.swift

    /// 全局的常量或变量都是延迟计算的,跟延迟存储属性相似,但全局的常量或变量不需要标记‘lazy’特性。
    private var wynCountdownControllers: [String: WynCountdownController] = [:]
    
    class WynCountdownController {
        /// 通过identifier来取得控制器实例
        static func shared(withIdentifier identifier: String) -> WynCountdownController {
    
            if let c = wynCountdownControllers[identifier] {
                return c
            } else {
                let c = WynCountdownController()
                c.identifier = identifier
                objc_sync_enter(wynCountdownControllers)
                wynCountdownControllers[identifier] = c
                objc_sync_exit(wynCountdownControllers)
    
                return c
            }
        }
        /// 只能通过shared(withIdentifier:)方法来实例化Controller
        private init() {}
    
        private var identifier: String!
    
        private var timer: Timer?
    
        /// 当前倒计时时间
        public var currentVal: Int = 60
    
        /// 当前状态
        public var isTicking = false
        /// 1秒1回调
        public var tick: ((Timer, Int) -> Void)!
        private func newTimer() {
            timer = Timer(fire: Date(), interval: 1, repeats: true) { [weak self](timer) in
    
                guard let strongSelf = self else { return }
    
                strongSelf.currentVal -= 1
                strongSelf.tick(timer, strongSelf.currentVal)
    
                if strongSelf.currentVal <= 0 {
                    strongSelf.isTicking = false
    
                    timer.invalidate()
                    strongSelf.timer = nil
                    objc_sync_enter(wynCountdownControllers)
                    wynCountdownControllers[strongSelf.identifier] = nil
                    objc_sync_exit(wynCountdownControllers)
    
                }
            }
        }
    
        public func begin(sec: Int, tickClosure: @escaping (Timer, Int) -> Void) {
            currentVal = sec
            tick = tickClosure
    
            newTimer()
            RunLoop.current.add(self.timer!, forMode: .common)
    
            isTicking = true
        }
    
        deinit {
            self.timer?.invalidate()
            self.timer = nil
        }
    }
    
    展开全文
  • override func viewDidLoad() { super.viewDidLoad() //写一个按钮 btn.frame.size = CGSize(width: 100, height: 50) btn.center = view.center btn.backgroundColor = .red ...
    override func viewDidLoad() {
            super.viewDidLoad()
            //写一个按钮
            btn.frame.size = CGSize(width: 100, height: 50)
            btn.center = view.center
            btn.backgroundColor = .red
            view.addSubview(btn)
            btn.addTarget(self, action: #selector(timeChange), for: .touchUpInside)
            btn.setTitle("发送验证码", for: .normal)
        }

    点击事件:

    @objc func timeChange() {
            
            var time = 10
            let codeTimer = DispatchSource.makeTimerSource(flags: .init(rawValue: 0), queue: DispatchQueue.global())
            codeTimer.schedule(deadline: .now(), repeating: .milliseconds(1000))  //此处方法与Swift 3.0 不同
            codeTimer.setEventHandler {
                
                time = time - 1
                
                DispatchQueue.main.async {
                    self.btn.isEnabled = false
                }
                
                if time < 0 {
                    codeTimer.cancel()
                    DispatchQueue.main.async {
                        self.btn.isEnabled = true
                        self.btn.setTitle("重新发送", for: .normal)
                    }
                    return
                }
                
                DispatchQueue.main.async {
                    self.btn.setTitle("\(time)", for: .normal)
                }
                
            }
            
            codeTimer.activate()
            
        }

    效果图:


    注意:点击事件中的 self.btn.isEnabled  self.btn.setTitle 须放在主线程中进行,不然控制台报错,截图如下:


    展开全文
  • Swift - UITableViewCell倒计时重用解决方案   效果   源码 https://github.com/YouXianMing/Swift-Animations // // CountDownTimerController.swift // Swift-Animations // // Created by ...

    Swift - UITableViewCell倒计时重用解决方案

     

    效果

     

    源码

    https://github.com/YouXianMing/Swift-Animations

    //
    //  CountDownTimerController.swift
    //  Swift-Animations
    //
    //  Created by YouXianMing on 16/9/4.
    //  Copyright © 2016年 YouXianMing. All rights reserved.
    //
    
    import UIKit
    
    class CountDownTimerController: NormalTitleViewController, UITableViewDelegate, UITableViewDataSource {
    
        var timesArray : [CellDataAdapter]!
        var tableView  : UITableView!
        var timer      : GCDTimer = GCDTimer(inQueue: GCDQueue.mainQueue)
        
        override func setup() {
            
            super.setup()
            
            // Create data source.
            timesArray = [CellDataAdapter]()
            
            func add(title title : String, countdownTime : Int) {
            
                timesArray.append(CountDownTimeCell.Adapter(data: TimeModel(title: title, countdownTime: countdownTime)))
            }
            
            add(title: "YouXianMing", countdownTime: 20034)
            add(title: "Aaron",       countdownTime: 31)
            add(title: "Nicholas",    countdownTime: 1003)
            add(title: "Quentin",     countdownTime: 394)
            add(title: "Samirah",     countdownTime: 345345)
            add(title: "Serafina",    countdownTime: 233)
            add(title: "Shanon",      countdownTime: 4649)
            add(title: "Sophie",      countdownTime: 3454)
            add(title: "Steven",      countdownTime: 54524)
            add(title: "Saadiya",     countdownTime: 235)
            
            // Create TableView.
            tableView                = UITableView(frame: (contentView?.bounds)!)
            tableView.delegate       = self
            tableView.dataSource     = self
            tableView.separatorStyle = .None
            tableView.rowHeight      = 60
            contentView?.addSubview(tableView)
    
            // Register cell.
            CountDownTimeCell.RegisterTo(tableView)
    
            // Timer event.
            weak var wself = self
            timer.event({ 
                
                for (_, dataAdapter) in wself!.timesArray.enumerate() {
                    
                    if let model = dataAdapter.data as? TimeModel {
                    
                        model.countDown()
                    }
                }
                
                DefaultNotificationCenter.PostMessageTo(NotificationEvent.CountDownTimeCellCountDown.Message())
            
                }, timeIntervalWithSeconds: 1.0)
            timer.start()
        }
        
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            
            return timesArray.count
        }
        
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            
            return tableView.dequeueCellAndLoadContentFromAdapter(timesArray[indexPath.row], indexPath: indexPath)
        }
        
        func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
            
            (cell as! CustomCell).display = true
        }
        
        func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
            
            (cell as! CustomCell).display = false
        }
    }

     

    展开全文
  • 实现倒计时的VIew,比如活动倒计时,验证码获取倒计时
  • 根据UIBezierPath和CAShapeLayer自定义倒计时进度条,适用于app启动的时候设置一个倒计时关闭启动页面。可以设置进度条颜色,填充颜色,进度条宽度以及点击事件等。
  • func changtime() { var timerout = 60 let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT...
  • ///  ... * 倒计时按钮的分装  *  *  *  */ import UIKit class XCTools: NSObject { // 定时器  var myTimer:NSTimer?  /// 倒计时按钮  func sen
  • swift 简单倒计时

    2017-01-07 23:31:15
    swift 原因写的倒计时,可用于接收短信验证码倒计时
  • swift版本的倒计时按钮,支持cocoapods,具体使用可以查看我的github:https://github.com/manofit/GJTimeButton,如果有用,麻烦给星~~~~
  • swift倒计时动画

    2019-10-30 10:17:05
    import Foundation class UploadView: UIView { var selfView :UIView? = nil @IBOutlet weak var verTip: UILabel! var uploadDic : Dictionary<String,Any>? = nil ...
  • 一、倒计时 @interface ViewController () { UIAlertController *alertview; NSString * message; NSTimer * waiTimer; NSInteger waitTctime; } @end @implementation ViewContro...
  • 在我们开发中常常会遇到注册、验证信息的需求这个时候我们需要向服务器发送请求来获得验证码,通常需要进行获取倒计时操作.
  • // sendBtn.swift // 自定义验证码按钮 // // Created by apple on 16/4/22. // Copyright © 2016年 崔家维. All rights reserved. // import UIKit let NUMSS = 60 protocol ...
  • 哇,一转眼都凌晨1:30了(可怜的我还在写博客,不过趁这段时间有空多谢谢,多总结总结是没有错的,希望能和大家一起进步),终于把今天,不对,昨天突然想实现的一个倒计时Label写好了,感觉像现在app中这种登陆啊...
  • 定义全局变量 var count : Int? var enabled : Bool? var timer : Timer? ...var codeBtn : UIButton?...let codeBtn = UIButton(type: UIButton.ButtonType.custom) ...codeBtn.setTitle("获取验证码", for: UIControl...
  • swift 封装按钮倒计时

    2019-08-01 17:08:03
    在项目开发中经常需要用到倒计时的功能,比如注册部分获取验证码时,这里通过extension(UIButton)的方式来实现倒计时的功能 直接上代码 var codeTimer = DispatchSource.makeTimerSource(queue:DispatchQueue....
  • class TCCountDown { private var countdownTimer: Timer? var codeBtn = UIButton() private var remainingSeconds: Int = 0 { willSet { co...
1 2 3 4 5 ... 20
收藏数 1,026
精华内容 410
关键字:

30分钟倒计时 swift