├── Clock.playground
├── contents.xcplayground
├── section-1.swift
└── timeline.xctimeline
├── README.md
└── screenshot.png
/Clock.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Clock.playground/section-1.swift:
--------------------------------------------------------------------------------
1 | // Clockground - noun: a place where people can clock
2 |
3 | import Cocoa
4 | import QuartzCore
5 | import XCPlayground
6 |
7 | class ClockView: NSView {
8 | let backgroundLayer = CAShapeLayer()
9 | let faceLayer = CAShapeLayer()
10 | let handsLayer = CAShapeLayer()
11 |
12 | let clockSize = CGFloat(256.0)
13 |
14 | override init() {
15 | super.init(frame: NSRect(x:0, y:0, width:clockSize, height:clockSize))
16 |
17 | self.backgroundLayer.frame = self.bounds
18 | self.faceLayer.frame = self.bounds
19 | self.handsLayer.frame = self.bounds
20 |
21 | setUpBackgroundLayer()
22 | setUpFaceLayer()
23 | setUpHandsLayer()
24 |
25 | // this is required to use layers in NSView,
26 | // different from default UIView behavior!
27 | self.wantsLayer = true
28 |
29 | self.layer?.addSublayer(self.backgroundLayer)
30 | self.layer?.addSublayer(self.faceLayer)
31 | self.layer?.addSublayer(self.handsLayer)
32 | }
33 |
34 | required init?(coder: NSCoder) {
35 | fatalError("init(coder:) has not been implemented")
36 | }
37 |
38 | func setUpBackgroundLayer() {
39 | let backgroundPath = NSBezierPath(roundedRect: self.bounds, xRadius: 45, yRadius: 45)
40 |
41 | self.backgroundLayer.path = CGPathFromNSBezierPath(backgroundPath)
42 | }
43 |
44 | func setUpFaceLayer() {
45 | let faceInset = CGFloat(15.0)
46 | let faceRadius = clockSize-(2*faceInset)
47 | let numberRadius = CGFloat(90.0)
48 | let numberWidth = CGFloat(22.0)
49 | let numberHeight = CGFloat(22.0)
50 |
51 | let facePath = NSBezierPath(ovalInRect: NSRect(x:faceInset, y:faceInset, width:faceRadius, height:faceRadius))
52 |
53 | self.faceLayer.fillColor = NSColor.whiteColor().CGColor
54 | self.faceLayer.path = CGPathFromNSBezierPath(facePath)
55 |
56 | for hour in 1...12 {
57 | let hourtext = NSString(format: "%d", hour)
58 |
59 | let pct = Double(hour)/12
60 | let position = (pct*2*M_PI)-(M_PI_2)
61 |
62 | // XCPCaptureValue("sinvalue", sin(Double(position)))
63 | let xadj = CGFloat(cos(Double(position)))
64 | let yadj = CGFloat(sin(Double(position)))
65 | let xpos = (clockSize/2) + (xadj*numberRadius) - (numberWidth/2)
66 | let ypos = clockSize - ((clockSize/2) + (yadj*numberRadius) + (numberWidth/2))
67 |
68 | let text = CATextLayer()
69 | text.frame = CGRectMake(xpos, ypos, numberWidth, numberHeight)
70 | text.string = hourtext
71 | text.fontSize = 20
72 | text.font = NSFont(name: "HelveticaNeue-Light", size: 10)
73 | text.alignmentMode = kCAAlignmentCenter
74 | text.foregroundColor = NSColor.blackColor().CGColor
75 |
76 | self.faceLayer.addSublayer(text)
77 | }
78 | }
79 |
80 | func setUpHandsLayer() {
81 | // look up time for hand values
82 | let format = NSDateFormatter()
83 | let now = NSDate()
84 |
85 | format.dateFormat = "hh"
86 | let hour = format.stringFromDate(now).toInt()!
87 | let hourRotation = CGFloat((2.0*M_PI*(Double(hour)/12.0))+M_PI)
88 |
89 | format.dateFormat = "mm"
90 | let minute = format.stringFromDate(now).toInt()!
91 | let minuteRotation = CGFloat((2.0*M_PI*(Double(minute)/60.0))+M_PI)
92 |
93 | format.dateFormat = "ss"
94 | let second = format.stringFromDate(now).toInt()!
95 | let secondRotation = CGFloat((2.0*M_PI*(Double(second)/60.0))+M_PI)
96 |
97 | // style setup for the hands
98 | let darkCenter = CAShapeLayer()
99 | let darkSize = CGFloat(13.0)
100 | let redCenter = CAShapeLayer()
101 | redCenter.fillColor = NSColor.redColor().CGColor
102 | let redSize = CGFloat(5.0)
103 |
104 | // draw the black center circle
105 | let darkPath = NSBezierPath(ovalInRect: CGRectMake((clockSize/2)-(darkSize/2), (clockSize/2)-(darkSize/2), darkSize, darkSize))
106 | darkCenter.path = CGPathFromNSBezierPath(darkPath)
107 |
108 | self.handsLayer.addSublayer(darkCenter)
109 |
110 | // set up the minute hand
111 | let minuteWidth = CGFloat(3.0)
112 | let minuteHeight = CGFloat(75.0)
113 | let minuteStartPoint = CGPointMake((clockSize/2)-(minuteWidth/2), clockSize/2)
114 |
115 | // draw minute hand
116 | let minuteHand = CAShapeLayer()
117 | minuteHand.frame = CGRectMake(0, 0, clockSize, clockSize)
118 | minuteHand.fillColor = NSColor.blackColor().CGColor
119 |
120 | minuteHand.path = self.makeRectPath(minuteStartPoint, width: minuteWidth, height: minuteHeight)
121 |
122 | // anchor point is already 0.5,0.5 so we can just rotate
123 | minuteHand.transform = CATransform3DMakeRotation(minuteRotation, 0, 0, -1)
124 |
125 | self.handsLayer.addSublayer(minuteHand)
126 |
127 | // set up the hour hand
128 | let hourWidth = CGFloat(4.0)
129 | let hourHeight = CGFloat(35.0)
130 | let hourStartPoint = CGPointMake((clockSize/2)-(hourWidth/2), clockSize/2)
131 |
132 | // draw hour hand
133 | let hourHand = CAShapeLayer()
134 | hourHand.frame = CGRectMake(0, 0, clockSize, clockSize)
135 | hourHand.fillColor = NSColor.blackColor().CGColor
136 |
137 | hourHand.path = self.makeRectPath(hourStartPoint, width: hourWidth, height: hourHeight)
138 |
139 | // anchor point is already 0.5,0.5 so we can just rotate
140 | hourHand.transform = CATransform3DMakeRotation(hourRotation, 0, 0, -1)
141 |
142 | self.handsLayer.addSublayer(hourHand)
143 |
144 | // draw the red center bit over the black existing ones
145 | let redPath = NSBezierPath(ovalInRect: CGRectMake((clockSize/2)-(redSize/2), (clockSize/2)-(redSize/2), redSize, redSize))
146 | redCenter.path = CGPathFromNSBezierPath(redPath)
147 |
148 | self.handsLayer.addSublayer(redCenter)
149 |
150 | // set up the seconds hand
151 | let secondWidth = CGFloat(2.0)
152 | let secondHeight = CGFloat(65.0)
153 | let secondStartPoint = CGPointMake((clockSize/2)-(secondWidth/2), clockSize/2)
154 |
155 | // draw the seconds hand
156 | let secondHand = CAShapeLayer()
157 | secondHand.frame = CGRectMake(0, 0, clockSize, clockSize)
158 | secondHand.fillColor = NSColor.redColor().CGColor
159 |
160 | secondHand.path = self.makeRectPath(secondStartPoint, width: secondWidth, height: secondHeight)
161 |
162 | // anchor point is already 0.5,0.5 so we can just rotate
163 | secondHand.transform = CATransform3DMakeRotation(secondRotation, 0, 0, -1)
164 |
165 | // rotate half a circle in thirty seconds
166 | var rotationAnimation = CABasicAnimation(keyPath: "transform")
167 | rotationAnimation.duration = 30
168 | rotationAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
169 | // this tiny amount off ensures that we always rotate in the
170 | // correct direction around the circle
171 | rotationAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeRotation(secondRotation+CGFloat(M_PI)-(0.001), 0, 0, -1))
172 |
173 | secondHand.addAnimation(rotationAnimation, forKey: "rotationTransform")
174 |
175 | self.handsLayer.addSublayer(secondHand)
176 | }
177 |
178 | // convenience, makes a rectangular path
179 | func makeRectPath(start: CGPoint, width: CGFloat, height: CGFloat) -> CGPath {
180 |
181 | let path = CGPathCreateMutable()
182 | CGPathMoveToPoint(path, nil, start.x, start.y)
183 | CGPathAddLineToPoint(path, nil, start.x, start.y - height)
184 | CGPathAddLineToPoint(path, nil, start.x+width, start.y-height)
185 | CGPathAddLineToPoint(path, nil, start.x+width, start.y)
186 | CGPathCloseSubpath(path)
187 |
188 | return path
189 | }
190 | }
191 |
192 | // thanks WWDC session 408 :)
193 | func CGPathFromNSBezierPath(nspath: NSBezierPath) -> CGPath? {
194 | if nspath.elementCount == 0 {
195 | return nil
196 | }
197 |
198 | let path = CGPathCreateMutable()
199 | var didClosePath = false
200 |
201 | for i in 0..
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | swiftclock
2 | ==========
3 |
4 | 
5 |
6 | An iOS Clock icon in a swift playground. It reads the current time and animates the second hand for 30 seconds in the style of the iOS clock icon.
7 |
8 | Compatible with Xcode 6.1 thanks to [Corinne Krych](https://github.com/corinnekrych)
9 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nickoneill/swiftclock/20131e538940ac15c994eb27276aad9dc1906794/screenshot.png
--------------------------------------------------------------------------------