BorderDesignable.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. //
  2. // Created by Jake Lin on 11/18/15.
  3. // Copyright © 2015 IBAnimatable. All rights reserved.
  4. //
  5. import UIKit
  6. public protocol BorderDesignable: class {
  7. /**
  8. `bordertype: solid, dash, if not specified, solid will be used
  9. */
  10. var borderType: BorderType { get set }
  11. /**
  12. `border-color`, border color
  13. */
  14. var borderColor: UIColor? { get set }
  15. /**
  16. `border-width`, border width
  17. */
  18. var borderWidth: CGFloat { get set }
  19. /**
  20. border side: Top, Right, Bottom or Left, if not specified, all border sides will display,
  21. */
  22. var borderSides: BorderSides { get set }
  23. }
  24. // MARK: - UIView
  25. public extension BorderDesignable where Self: UIView {
  26. func configureBorder() {
  27. configureBorder(in: self)
  28. }
  29. }
  30. // MARK: - UITextField
  31. public extension BorderDesignable where Self: UITextField {
  32. func configureBorder() {
  33. if borderWidth > 0 {
  34. borderStyle = .none
  35. }
  36. configureBorder(in: self)
  37. }
  38. }
  39. // MARK: - Common
  40. extension BorderDesignable {
  41. func configureBorder(in view: UIView) {
  42. guard borderColor != nil, borderWidth > 0 else {
  43. clearLayer(in: view)
  44. return
  45. }
  46. clearLayer(in: view)
  47. if let mask = view.layer.mask as? CAShapeLayer {
  48. applyBorderOnMask(mask, in: view)
  49. } else {
  50. drawBorders(in: view)
  51. }
  52. }
  53. }
  54. // MARK: - Layer
  55. extension BorderDesignable {
  56. private func clearLayer(in view: UIView) {
  57. view.layer.borderColor = nil
  58. view.layer.borderWidth = 0
  59. view.layer.sublayers?.filter { $0.name == "borderSideLayer" || $0.name == "borderAllSides" }
  60. .forEach { $0.removeFromSuperlayer() }
  61. }
  62. private func applyBorderOnMask(_ mask: CAShapeLayer, in view: UIView) {
  63. let borderLayer = CAShapeLayer()
  64. borderLayer.name = "borderAllSides"
  65. borderLayer.path = mask.path
  66. borderLayer.fillColor = UIColor.clear.cgColor
  67. borderLayer.strokeColor = borderColor!.cgColor
  68. borderLayer.lineWidth = borderWidth
  69. borderLayer.frame = view.bounds
  70. view.layer.insertSublayer(borderLayer, at: 0)
  71. }
  72. }
  73. // MARK: - Drawing
  74. extension BorderDesignable {
  75. private func drawBorders(in view: UIView) {
  76. if borderType == .solid, borderSides == .AllSides {
  77. view.layer.borderColor = borderColor!.cgColor
  78. view.layer.borderWidth = borderWidth
  79. } else {
  80. drawBordersSides(in: view)
  81. }
  82. }
  83. private func drawBordersSides(in view: UIView) {
  84. let shapeLayer = CAShapeLayer()
  85. shapeLayer.name = "borderSideLayer"
  86. shapeLayer.path = makeBorderPath(in: view.bounds).cgPath
  87. shapeLayer.fillColor = UIColor.clear.cgColor
  88. shapeLayer.strokeColor = borderColor!.cgColor
  89. shapeLayer.lineWidth = borderWidth
  90. shapeLayer.frame = view.bounds
  91. switch borderType {
  92. case let .dash(dashLength, spaceLength):
  93. shapeLayer.lineJoin = CAShapeLayerLineJoin.round
  94. shapeLayer.lineDashPattern = [dashLength as NSNumber, spaceLength as NSNumber]
  95. case .solid, .none:
  96. break
  97. }
  98. view.layer.insertSublayer(shapeLayer, at: 0)
  99. }
  100. private func makeBorderPath(in bounds: CGRect) -> UIBezierPath {
  101. let lines = makeLines(in: bounds)
  102. let borderPath = UIBezierPath()
  103. lines.forEach {
  104. borderPath.move(to: $0.start)
  105. borderPath.addLine(to: $0.end)
  106. }
  107. return borderPath
  108. }
  109. private func makeLines(in bounds: CGRect) -> [(start: CGPoint, end: CGPoint)] {
  110. let shift = borderWidth / 2
  111. var lines = [(start: CGPoint, end: CGPoint)]()
  112. if borderSides.contains(.top) {
  113. lines.append((start: CGPoint(x: 0, y: shift), end: CGPoint(x: bounds.size.width, y: shift)))
  114. }
  115. if borderSides.contains(.right) {
  116. lines.append((start: CGPoint(x: bounds.size.width - shift, y: 0), end: CGPoint(x: bounds.size.width - shift, y: bounds.size.height)))
  117. }
  118. if borderSides.contains(.bottom) {
  119. lines.append((start: CGPoint(x: 0, y: bounds.size.height - shift), end: CGPoint(x: bounds.size.width, y: bounds.size.height - shift)))
  120. }
  121. if borderSides.contains(.left) {
  122. lines.append((start: CGPoint(x: shift, y: 0), end: CGPoint(x: shift, y: bounds.size.height)))
  123. }
  124. return lines
  125. }
  126. }