一、前言
上一篇,我们完成了第一个自定义组件《圆形倒计时进度条》,而且支持 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 + 广告页 + 第一个组件:圆形倒计时进度条】我们就完全分享完了,接下来,开始进入我们的正文:首页。
目前为止所有源码:《传递门》
有任何问题,欢迎交流,谢谢!
网友评论