Swift——圖層幾何學(時鐘)
最近在看iOS核心動畫高階技巧,OC語言與Swift轉換有點令人頭疼,某些類和Api改動相當大。所以遇到我很困擾的語法我都會記錄一下,也方便日後回顧。
首先是關於時間的類NSCalendar

時鐘.gif
這是iOS核心動畫高階技巧——3.圖層幾何學——3.2錨點裡的原文
- (void)tick { //convert time to hours, minutes and seconds NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; NSDateComponents *components = [calendar components:units fromDate:[NSDate date]]; CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0; //calculate hour hand angle //calculate minute hand angle CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0; //calculate second hand angle CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0; //rotate hands self.hourHand.transform = CGAffineTransformMakeRotation(hoursAngle); self.minuteHand.transform = CGAffineTransformMakeRotation(minsAngle); self.secondHand.transform = CGAffineTransformMakeRotation(secsAngle); }
在我前前後後的各種查詢語法糖的情況下,摸索出三種寫法。
第一種
因為這句程式碼 NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit
中或的寫法,是在是不知道怎麼轉,所以就暫時沒管它
@objc func tick(){ let calendar = NSCalendar.init(identifier: NSCalendar.Identifier.gregorian) let hoursAngle = (getComponents(calendar!, unit: .hour) / 12.0) * CGFloat.pi * 2.0 let minsAngle = (getComponents(calendar!, unit: .minute) / 60.0) * CGFloat.pi * 2.0 let secsAngle = (getComponents(calendar!, unit: .second) / 60.0) * CGFloat.pi * 2.0 self.hourHand.transform = CGAffineTransform.init(rotationAngle: hoursAngle) self.minuteHand.transform = CGAffineTransform.init(rotationAngle: minsAngle) self.secondHand.transform = CGAffineTransform.init(rotationAngle: secsAngle) } func getComponents(_ cal:NSCalendar, unit:NSCalendar.Unit) -> CGFloat{ return CGFloat(cal.component(unit, from: Date())) }
第二種
後來總是想著,OC可以Swift肯定也行的,然後發現是這樣寫的
@objc func tick2(){ let calendar = NSCalendar.init(identifier: NSCalendar.Identifier.gregorian) let units:NSCalendar.Unit = [.hour, .minute, .second] let comp = calendar!.components(units, from: Date()) setDigit(digit: comp.hour! / 10, forView: self.digitViews[0]) setDigit(digit: comp.hour! % 10, forView: self.digitViews[1]) setDigit(digit: comp.minute! / 10, forView: self.digitViews[2]) setDigit(digit: comp.minute! % 10, forView: self.digitViews[3]) setDigit(digit: comp.second! / 10, forView: self.digitViews[4]) setDigit(digit: comp.second! % 10, forView: self.digitViews[5]) }
第三種
NS開頭的,Swift肯定是會重寫成自己的類的,試試?
@objc func tick3(){ let calendar = Calendar.init(identifier: .gregorian) let ccc:Set<Calendar.Component> = [Calendar.Component.hour, Calendar.Component.minute, Calendar.Component.second] let comp = calendar.dateComponents(ccc, from: Date()) setDigit(digit: comp.hour! / 10, forView: self.digitViews[0]) setDigit(digit: comp.hour! % 10, forView: self.digitViews[1]) setDigit(digit: comp.minute! / 10, forView: self.digitViews[2]) setDigit(digit: comp.minute! % 10, forView: self.digitViews[3]) setDigit(digit: comp.second! / 10, forView: self.digitViews[4]) setDigit(digit: comp.second! % 10, forView: self.digitViews[5]) }
這些程式碼只是語法的轉換,不是完整程式碼。而且程式碼量很小,我也不想上傳github,所以直接貼出來,但是沒包括Interface Builder中的佈局,所以我截了張XIB的圖出來。還有不要給約束。

XIB截圖
程式碼
class ClockController: UIViewController { @IBOutlet weak var hourHand: UIImageView! @IBOutlet weak var minuteHand: UIImageView! @IBOutlet weak var secondHand: UIImageView! var timer:Timer! var timer2:Timer! @IBOutlet var digitViews:[UIView]! override func viewDidLoad() { super.viewDidLoad() self.secondHand.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.9) self.minuteHand.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.9) self.hourHand.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.9) timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true) // Do any additional setup after loading the view. tick() setupTick2() } @objc func tick(){ let calendar = NSCalendar.init(identifier: NSCalendar.Identifier.gregorian) let hoursAngle = (getComponents(calendar!, unit: .hour) / 12.0) * CGFloat.pi * 2.0 let minsAngle = (getComponents(calendar!, unit: .minute) / 60.0) * CGFloat.pi * 2.0 let secsAngle = (getComponents(calendar!, unit: .second) / 60.0) * CGFloat.pi * 2.0 self.hourHand.transform = CGAffineTransform.init(rotationAngle: hoursAngle) self.minuteHand.transform = CGAffineTransform.init(rotationAngle: minsAngle) self.secondHand.transform = CGAffineTransform.init(rotationAngle: secsAngle) } func getComponents(_ cal:NSCalendar, unit:NSCalendar.Unit) -> CGFloat{ return CGFloat(cal.component(unit, from: Date())) } func setupTick2(){ let digits = UIImage.init(named: "Digits") for vi in self.digitViews { vi.layer.contents = digits?.cgImage vi.layer.contentsRect = CGRect.init(x: 0, y: 0, width: 0.1, height: 1.0) vi.layer.contentsGravity = kCAGravityResizeAspect vi.layer.magnificationFilter = kCAFilterNearest } timer2 = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick2), userInfo: nil, repeats: true) self.tick2() } @objc func tick2(){ let calendar = NSCalendar.init(identifier: NSCalendar.Identifier.gregorian) let units:NSCalendar.Unit = [.hour, .minute, .second] let comp = calendar!.components(units, from: Date()) setDigit(digit: comp.hour! / 10, forView: self.digitViews[0]) setDigit(digit: comp.hour! % 10, forView: self.digitViews[1]) setDigit(digit: comp.minute! / 10, forView: self.digitViews[2]) setDigit(digit: comp.minute! % 10, forView: self.digitViews[3]) setDigit(digit: comp.second! / 10, forView: self.digitViews[4]) setDigit(digit: comp.second! % 10, forView: self.digitViews[5]) } @objc func tick3(){ let calendar = Calendar.init(identifier: .gregorian) let ccc:Set<Calendar.Component> = [Calendar.Component.hour, Calendar.Component.minute, Calendar.Component.second] let comp = calendar.dateComponents(ccc, from: Date()) setDigit(digit: comp.hour! / 10, forView: self.digitViews[0]) setDigit(digit: comp.hour! % 10, forView: self.digitViews[1]) setDigit(digit: comp.minute! / 10, forView: self.digitViews[2]) setDigit(digit: comp.minute! % 10, forView: self.digitViews[3]) setDigit(digit: comp.second! / 10, forView: self.digitViews[4]) setDigit(digit: comp.second! % 10, forView: self.digitViews[5]) } func setDigit(digit:Int, forView view:UIView){ let num = Double(digit) view.layer.contentsRect = CGRect.init(x: num * 0.1, y: 0, width: 0.1, height: 1.0) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }