假设你想让每个"点"在点击时开始"从60开始倒计时",这里有一个简单的例子……
我们将创建一个带有标签和圆形边框的UIView
子类.
它还将有一个"startTime"属性:
private var startTime: Date?
当我们点击视图时,我们将开始时间设置为"Now":
startTime = Date()
我们将有一个函数来更新倒计时:
public func timerFired(_ now: Date) {
if let st = startTime {
let elapsedSeconds = Int(floor(now.timeIntervalSinceReferenceDate - st.timeIntervalSinceReferenceDate))
label.text = "\(60 - elapsedSeconds)"
// if we've reached 60-seconds, set startTime to nil
// so we don't keep going to -1, -2, -3 etc
if elapsedSeconds == 60 {
self.startTime = nil
}
}
}
在我们的控制器中,我们将有一个每隔0.3秒触发的single次重复计时器.如果我们每秒只emits 一次,我们就可以"跳过"数字:
// use less-than 1-second for the timeInterval... otherwise, we *could* end up with
// skipped seconds. That is, we might see "60 59 58 56 55 53 etc"
Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(timerFunc), userInfo: nil, repeats: true)
每次触发计时器函数时,我们都会通过呼叫.timerFired()
来告诉每个"Spot"视图,然后"Spot"视图会将自己的startTime
与新时间进行比较,并更新其标签.
因此,下面是我们的定制SpotView
级:
class SpotView: UIView {
private var startTime: Date?
private let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
label.numberOfLines = 0
label.text = "Unlock\nto See"
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor),
])
layer.borderColor = UIColor.red.cgColor
layer.borderWidth = 1
let g = UITapGestureRecognizer(target: self, action: #selector(gotTap(_:)))
addGestureRecognizer(g)
}
override func layoutSubviews() {
super.layoutSubviews()
let m: CGFloat = min(bounds.width, bounds.height)
layer.cornerRadius = m * 0.5
}
@objc func gotTap(_ g: UITapGestureRecognizer) {
// set the startTime property to "now"
startTime = Date()
label.text = "60"
}
public func timerFired(_ now: Date) {
if let st = startTime {
let elapsedSeconds = Int(floor(now.timeIntervalSinceReferenceDate - st.timeIntervalSinceReferenceDate))
label.text = "\(60 - elapsedSeconds)"
// if we've reached 60-seconds, set startTime to nil
// so we don't keep going to -1, -2, -3 etc
if elapsedSeconds == 60 {
self.startTime = nil
}
}
}
}
和一个样本控制器:
class FourSpotsVC: UIViewController {
var theSpots: [SpotView] = []
override func viewDidLoad() {
super.viewDidLoad()
let w: CGFloat = 100.0
let h: CGFloat = 100.0
var x: CGFloat = 40.0
var y: CGFloat = 100.0
// let's add 4 "Spot" views
for i in 0..<4 {
x = i % 2 == 0 ? 40.0 : 160.0
let v = SpotView()
v.frame = CGRect(x: x, y: y, width: w, height: h)
view.addSubview(v)
theSpots.append(v)
y += h + 20.0
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// use less-than 1-second for the timeInterval... otherwise, we *could* end up with
// skipped seconds. That is, we might see "60 59 58 56 55 53 etc"
Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(timerFunc), userInfo: nil, repeats: true)
}
@objc func timerFunc() {
let n = Date()
theSpots.forEach { v in
v.timerFired(n)
}
}
}
run 时是这样的(我点击了第一个点,然后等待了大约10秒,然后点击第二个点):