ActivityIndicatorShape.swift 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. //
  2. // Created by Tom Baranes on 21/08/16.
  3. // Copyright © 2016 IBAnimatable. All rights reserved.
  4. //
  5. import UIKit
  6. enum ActivityIndicatorShape {
  7. case circle
  8. case circleSemi
  9. case ring
  10. case ringTwoHalfVertical
  11. case ringTwoHalfHorizontal
  12. case ringThirdFour
  13. case rectangle
  14. case triangle
  15. case offsetTriangle
  16. case line
  17. case pacman
  18. case stroke
  19. case mask(type: MaskType)
  20. }
  21. extension ActivityIndicatorShape {
  22. func makeLayer(size: CGSize, color: UIColor) -> CALayer {
  23. let path = self.path(size: size)
  24. let drawingMode = self.drawingMode(size: size)
  25. return ActivityIndicatorShape.makeShapeLayer(with: size,
  26. color: color,
  27. path: path,
  28. mode: drawingMode)
  29. }
  30. func path(size: CGSize) -> UIBezierPath {
  31. switch self {
  32. case .circle:
  33. return makeCirclePath(with: size)
  34. case .circleSemi:
  35. return makeSemiCirclePath(with: size)
  36. case .ring:
  37. return makeRingPath(with: size)
  38. case .ringTwoHalfVertical:
  39. return makeRingTwoHalfVerticalPath(with: size)
  40. case .ringTwoHalfHorizontal:
  41. return makeRingTwoHalfHorizontalPath(with: size)
  42. case .ringThirdFour:
  43. return makeRingThirdFourPath(with: size)
  44. case .rectangle:
  45. return makeRectanglePath(with: size)
  46. case .triangle:
  47. return makeTrianglePath(with: size)
  48. case .offsetTriangle:
  49. return makeOffsetTrianglePath(with: size)
  50. case .line:
  51. return makeLinePath(with: size)
  52. case .pacman:
  53. return makePacmanPath(with: size)
  54. case .stroke:
  55. return makeStrokePath(with: size)
  56. case .mask(let type):
  57. return makeMaskPath(with: size, type: type)
  58. }
  59. }
  60. fileprivate func drawingMode(size: CGSize) -> CAShapeLayer.DrawingMode {
  61. switch self {
  62. case .ring, .ringTwoHalfVertical, .ringTwoHalfHorizontal, .ringThirdFour, .stroke:
  63. return .stroke(lineWidth: 2)
  64. case .pacman:
  65. return .stroke(lineWidth: size.width / 2)
  66. default:
  67. return .fill
  68. }
  69. }
  70. }
  71. private extension ActivityIndicatorShape {
  72. static func makeShapeLayer(with size: CGSize, color: UIColor, path: UIBezierPath, mode: CAShapeLayer.DrawingMode) -> CAShapeLayer {
  73. let layer = CAShapeLayer()
  74. layer.apply(mode: mode, color: color)
  75. layer.backgroundColor = nil
  76. layer.path = path.cgPath
  77. layer.frame = size.rect
  78. return layer
  79. }
  80. }
  81. // MARK: - Circles
  82. private extension ActivityIndicatorShape {
  83. func makeCirclePath(with size: CGSize) -> UIBezierPath {
  84. let path = UIBezierPath()
  85. path.addArc(withCenter: size.center,
  86. radius: size.width / 2,
  87. startAngle: 0,
  88. endAngle: 2 * CGFloat.pi,
  89. clockwise: false)
  90. return path
  91. }
  92. func makeSemiCirclePath(with size: CGSize) -> UIBezierPath {
  93. let path = UIBezierPath()
  94. path.addArc(withCenter: size.center,
  95. radius: size.width / 2,
  96. startAngle: -CGFloat.pi / 6,
  97. endAngle: -5 * CGFloat.pi / 6,
  98. clockwise: false)
  99. path.close()
  100. return path
  101. }
  102. }
  103. // MARK: - Rings
  104. private extension ActivityIndicatorShape {
  105. func makeRingPath(with size: CGSize) -> UIBezierPath {
  106. let path = UIBezierPath()
  107. path.addArc(withCenter: size.center,
  108. radius: size.width / 2,
  109. startAngle: 0,
  110. endAngle: 2 * CGFloat.pi,
  111. clockwise: false)
  112. return path
  113. }
  114. func makeRingTwoHalfVerticalPath(with size: CGSize) -> UIBezierPath {
  115. let path = UIBezierPath()
  116. path.addArc(withCenter: size.center,
  117. radius: size.width / 2,
  118. startAngle: -3 * CGFloat.pi / 4,
  119. endAngle: -CGFloat.pi / 4,
  120. clockwise: true)
  121. path.move(
  122. to: CGPoint(x: size.width / 2 - size.width / 2 * cos(.pi / 4),
  123. y: size.height / 2 + size.height / 2 * sin(.pi / 4))
  124. )
  125. path.addArc(withCenter: size.center,
  126. radius: size.width / 2,
  127. startAngle: -5 * .pi / 4,
  128. endAngle: -7 * .pi / 4,
  129. clockwise: false)
  130. return path
  131. }
  132. func makeRingTwoHalfHorizontalPath(with size: CGSize) -> UIBezierPath {
  133. let path = UIBezierPath()
  134. path.addArc(withCenter: size.center,
  135. radius: size.width / 2,
  136. startAngle: 3 * .pi / 4,
  137. endAngle: 5 * .pi / 4,
  138. clockwise: true)
  139. path.move(
  140. to: CGPoint(x: size.width / 2 + size.width / 2 * cos(.pi / 4),
  141. y: size.height / 2 - size.height / 2 * sin(.pi / 4))
  142. )
  143. path.addArc(withCenter: size.center,
  144. radius: size.width / 2,
  145. startAngle: -.pi / 4,
  146. endAngle: .pi / 4,
  147. clockwise: true)
  148. return path
  149. }
  150. func makeRingThirdFourPath(with size: CGSize) -> UIBezierPath {
  151. let path = UIBezierPath()
  152. path.addArc(withCenter: size.center,
  153. radius: size.width / 2,
  154. startAngle: -3 * .pi / 4,
  155. endAngle: -.pi / 4,
  156. clockwise: false)
  157. return path
  158. }
  159. }
  160. // MARK: - Others
  161. private extension ActivityIndicatorShape {
  162. func makeRectanglePath(with size: CGSize) -> UIBezierPath {
  163. return UIBezierPath(rect: size.rect)
  164. }
  165. func makeOffsetTrianglePath(with size: CGSize) -> UIBezierPath {
  166. let path = UIBezierPath()
  167. let offsetY = size.height / 4
  168. path.move(to: CGPoint(x: 0, y: size.height - offsetY))
  169. path.addLine(to: CGPoint(x: size.width / 2, y: size.height / 2 - offsetY))
  170. path.addLine(to: CGPoint(x: size.width, y: size.height - offsetY))
  171. path.close()
  172. return path
  173. }
  174. func makeTrianglePath(with size: CGSize) -> UIBezierPath {
  175. let path = UIBezierPath()
  176. path.move(to: size.bottomLeft)
  177. path.addLine(to: size.center)
  178. path.addLine(to: size.bottomRight)
  179. path.close()
  180. return path
  181. }
  182. func makeLinePath(with size: CGSize) -> UIBezierPath {
  183. let path = UIBezierPath(roundedRect: size.rect,
  184. cornerRadius: size.width / 2)
  185. return path
  186. }
  187. func makeStrokePath(with size: CGSize) -> UIBezierPath {
  188. let path = UIBezierPath()
  189. path.addArc(withCenter: size.center,
  190. radius: size.width / 2,
  191. startAngle: -(.pi / 2),
  192. endAngle: .pi + .pi / 2,
  193. clockwise: true)
  194. return path
  195. }
  196. func makePacmanPath(with size: CGSize) -> UIBezierPath {
  197. let path = UIBezierPath()
  198. path.addArc(withCenter: size.center,
  199. radius: size.width / 4,
  200. startAngle: 0,
  201. endAngle: 2 * .pi,
  202. clockwise: true)
  203. return path
  204. }
  205. func makeMaskPath(with size: CGSize, type: MaskType) -> UIBezierPath {
  206. return type.bezierPath(in: size.rect)
  207. }
  208. }
  209. // MARK: - CAShapeLayer
  210. extension CAShapeLayer {
  211. enum DrawingMode {
  212. case fill
  213. case stroke(lineWidth: CGFloat)
  214. case fillStroke(lineWidth: CGFloat)
  215. }
  216. func apply(mode: DrawingMode, color: UIColor) {
  217. switch mode {
  218. case .fill:
  219. self.fillColor = color.cgColor
  220. case .stroke(let lineWidth):
  221. self.strokeColor = color.cgColor
  222. self.lineWidth = lineWidth
  223. self.fillColor = nil
  224. case .fillStroke(let lineWidth):
  225. self.strokeColor = color.cgColor
  226. self.lineWidth = lineWidth
  227. self.fillColor = color.cgColor
  228. }
  229. }
  230. }
  231. private extension CGSize {
  232. var rect: CGRect {
  233. return CGRect(origin: .zero, size: self)
  234. }
  235. var center: CGPoint {
  236. return CGPoint(x: self.width / 2, y: self.height / 2)
  237. }
  238. var bottomRight: CGPoint {
  239. return CGPoint(x: self.width, y: self.height)
  240. }
  241. var bottomLeft: CGPoint {
  242. return CGPoint(x: 0, y: self.height)
  243. }
  244. }