MaskDesignable.swift 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. //
  2. // Created by Jake Lin on 12/13/15.
  3. // Copyright © 2015 IBAnimatable. All rights reserved.
  4. //
  5. import UIKit
  6. /// A protocol provides mask designable feature.
  7. public protocol MaskDesignable: class {
  8. /**
  9. The type of the mask used for masking an IBAnimatable UI element.
  10. When you create a class and conform to `MaskDesignable`, you need to implement this computed property like
  11. ```
  12. public var maskType: MaskType = .none {
  13. didSet {
  14. configureMask()
  15. configureBorder()
  16. }
  17. }
  18. ```
  19. Because Interface Builder doesn't support `enum` for `@IBInspectable` property. You need to create an `internal` property using optional String as the type like
  20. ```
  21. @IBInspectable var _maskType: String? {
  22. didSet {
  23. maskType = MaskType(string: _maskType)
  24. }
  25. }
  26. ```
  27. */
  28. var maskType: MaskType { get set }
  29. }
  30. // MARK: - UIView
  31. extension MaskDesignable where Self: UIView {
  32. /// Mask the IBAnimatable UI element with provided `maskType`
  33. public func configureMask(previousMaskType: MaskType) {
  34. configureMask(in: self, previousMaskType: previousMaskType)
  35. }
  36. }
  37. // MARK: - Common
  38. extension MaskDesignable {
  39. func configureMask(in view: UIView, previousMaskType: MaskType) {
  40. if case .none = maskType {
  41. removePreviousMask(previousMaskType, in: view)
  42. } else {
  43. let path = maskType.bezierPath(in: view.bounds)
  44. draw(path, in: view)
  45. }
  46. }
  47. private func removePreviousMask(_ previousMaskType: MaskType, in view: UIView) {
  48. // If `previousMaskType` is `.none`, then we will **not** remove `layer.mask` before re-adding it. This allows for custom masks to be preserved.
  49. if case .none = previousMaskType {
  50. return
  51. } else {
  52. view.layer.mask?.removeFromSuperlayer()
  53. }
  54. }
  55. /**
  56. Draw a Bezier path on `layer.mask` using `CAShapeLayer`.
  57. - Parameter path: The Bezier path to draw.
  58. */
  59. private func draw(_ path: UIBezierPath, in view: UIView) {
  60. view.layer.mask?.removeFromSuperlayer()
  61. let maskLayer = CAShapeLayer()
  62. maskLayer.frame = CGRect(origin: .zero, size: view.bounds.size)
  63. maskLayer.path = path.cgPath
  64. if path.usesEvenOddFillRule {
  65. maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
  66. }
  67. view.layer.mask = maskLayer
  68. }
  69. }