美文网首页
iOS Swift5从0到1系列(十):完善广告页 + 常用定时

iOS Swift5从0到1系列(十):完善广告页 + 常用定时

作者: 青叶小小 | 来源:发表于2021-03-18 00:11 被阅读0次

    一、前言

    上一篇,我们完成了第一个自定义组件《圆形倒计时进度条》,而且支持 CocoaPods 和 SPM 两种方式,只是没有形成单独的库而已;本篇如题所述,我们将组件引入到广告页,来完善这个页面;当然,如果本篇只是完善该页面,可能会被喷太水,毕竟,使用还是相对来说较简单的,没什么难度。因此,我也顺便把第八篇中,虽然给出了源码,但没有分析的内容(定时器)分析下,以及通过代码来写约束需要注意的点,以及原因也一并分析给大家。

    二、完善广告页

    为了偷懒,我就采用 SPM 的本地集成方式,直接将整个目录拖进工程,然后添加 framework 即可。

    2.1、打开 AdvertiseViewController,import & lazy init

    import CircleProgressView
    class AdvertiseViewController: BaseViewController {
        ......
        // 延迟初始化 View,并且不写死位置及大小,由后面的约束来给定
        lazy var progress: CircleProgressView = CircleProgressView(frame: CGRect.zero)
        ......
    }
    

    2.2、放置圆形倒计时进度条控件

    class AdvertiseViewController: BaseViewController {
            func countDown() {
                progress.translatesAutoresizingMaskIntoConstraints = false
        
                // 允许用户点击后直接跳过进入下一页
                progress.isUserInteractionEnabled = true
                progress.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.terminer)))
                view.addSubview(progress)
                
                // 约束:
                // 1. 宽高均 64;
                // 2. 距离屏幕上边缘(top margin) = 50
                // 3. 距离屏幕右边缘(trailing margin) = -30
                NSLayoutConstraint.activate([
                    progress.widthAnchor.constraint(equalToConstant: 64.0),
                    progress.heightAnchor.constraint(equalToConstant: 64.0),
                    progress.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
                    progress.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30)
                ])
                ......
        }
    }
    

    2.3、修改viewDidLoad,启动倒计时 & 开始动画

    class AdvertiseViewController: BaseViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .kRed
            countDown() // timeCountDown() 改为 countDown()
        }
        
        func countDown() {
            ......
            // 开始动画,时长为倒计时,动画从 0.0 -> 1.0
            progress.setProgress(1.0, duration: Double(seconds), animated: true)
            
            // 开始倒计时
            timeCountDown()
        }
    }
    

    2.4、倒计时的过程中,我们的读秒也要不断变化

    class AdvertiseViewController: BaseViewController {
        func timeCountDown() {
            timer.schedule(deadline: .now(), repeating: .seconds(1))
            timer.setEventHandler(handler: {
                DispatchQueue.main.async { [weak self] in
                    
                    // 小于等于 0 时,结束 timer,并进行两个 window 的切换
                    if self!.seconds <= 0 {
                        self!.terminer()
                    } else {
                        // 倒计时中,不断设置 label 的内容
                        self!.progress.setContent(String(self!.seconds))
                    }
                    self!.seconds -= 1
                }
            })
            timer.resume()
        }
    }
    

    2.5、最后,还要修改我们的终止方法(添加 @objc)

    class AdvertiseViewController: BaseViewController {
        @objc func terminer() {
            ......
        }
    }
    

    三、约束布局注意事项

    不知道大家有没有注意到以下代码:

    class AdvertiseViewController: BaseViewController {
        func countDown() {
            progress.translatesAutoresizingMaskIntoConstraints = false
            ......
            view.addSubview(progress)
    
            NSLayoutConstraint.activate([
                ......
            ])
            ......
        }
    }
    

    我们在代码设置约束之前,都需要将 view 的 translatesAutoresizingMaskIntoConstraints 属性设置为 false ?当然,如果你之前一直用 XIB 来布局,设置约束的,可能不会注意到,但你应该多少知道吧。而且,就是使用 XIB ,也会存在代码布局的情况,所以,如果你告诉我,大家都这么做,所以,你也这么做,那么正好,本篇将会浅显的分析一下,帮助你更加了解背后的原因。

    一般,我们创建 view 有两种方式:

    • 通过代码来创建 view(translatesAutoresizingMaskIntoConstraints 默认是 OC: YES / Swift: true,下同);
    • 通过 IB 来创建 view(translatesAutoresizingMaskIntoConstraints 默认是(autoresize布局: YES, autolayout布局: NO));

    上面的代码中,我们使用的是约束布局(autolayout)。

    那为什么 translatesAutoresizingMaskIntoConstraints 使用约束布局时候,就要设置为 false(OC: NO)?

    因为 translatesAutoresizingMaskIntoConstraints 的本意是将 frame 布局自动转化为约束布局,转化的结果是为这个视图自动添加所有需要的约束,如果我们这时给视图添加自己创建的约束就一定会产生约束冲突。

    四、常用定时器(仅限使用,不展开)

    在 iOS 中,常用的定时器有以下几种:

    • NSTimer;
    • CADisplayLink;
    • GCD;
    • 其它方式的模拟
    优点 缺点
    NSTimer 使用简单 受Runloop影响会导致计时不精准
    CADisplayLink 精度高 CPU负载的时候会影响触发事件,且触发事件大于触发间隔会导致掉帧现象。
    GCD 较精准 基本不受其他影响

    我们的广告页,用的定时器实际上就是 GCD,它是一种较底层的方式,依赖于系统内核定时器,至于其它,本文不展开,主流常用的一般首选 GCD,因为较为精准,且不会有循环引用、内存泄露等问题。

    在 Swift4 以后,创建 GCD 定时器,要简单很多,只需一句话:

    // timer 关联一个队列(可以是全局 .global(),也可以是 .main,或其它)
    // 之后,timer.setEventHandler 可以将要处理的事件放入这个队列
    lazy var timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.global())
    

    我们在之后,可以调用 schedule 来设置 timer 何时开始,间隔多久,是否允许 delay 及可接受 delay 多久:

    // deadline 为启动时间,比如:立即启动 .now()
    // repeating 为间隔时间,如果是 .never 或 .default 则只会触发一次
    // leeway 在某些情况下(例如:非常耗时的操作)导致延迟了,那么允许放宽多久
    @available(swift 4)
    public func schedule(deadline: DispatchTime, 
                         repeating interval: DispatchTimeInterval = .never, 
                         leeway: DispatchTimeInterval = .nanoseconds(0))
    

    因为我们的 timer 已经创建,只是还没运行,所以启动方法:

    timer.resume()
    

    挂起则是:

    timer.suspend()
    

    取消则是:

    timer.cancel()
    

    至此,【双 window + 广告页 + 第一个组件:圆形倒计时进度条】我们就完全分享完了,接下来,开始进入我们的正文:首页。

    目前为止所有源码:《传递门》

    有任何问题,欢迎交流,谢谢!

    相关文章

      网友评论

          本文标题:iOS Swift5从0到1系列(十):完善广告页 + 常用定时

          本文链接:https://www.haomeiwen.com/subject/yavdcltx.html