BasalStateView.swift 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. //
  2. // BasalStateView.swift
  3. // Naterade
  4. //
  5. // Created by Nathan Racklyeft on 5/12/16.
  6. // Copyright © 2016 Nathan Racklyeft. All rights reserved.
  7. //
  8. import UIKit
  9. import SwiftUI
  10. public struct BasalStateSwiftUIView: UIViewRepresentable {
  11. var netBasalPercent: Double
  12. public init(netBasalPercent: Double) {
  13. self.netBasalPercent = netBasalPercent
  14. }
  15. public func makeUIView(context: UIViewRepresentableContext<BasalStateSwiftUIView>) -> BasalStateView {
  16. let view = BasalStateView()
  17. view.netBasalPercent = netBasalPercent
  18. return view
  19. }
  20. public func updateUIView(_ uiView: BasalStateView, context: UIViewRepresentableContext<BasalStateSwiftUIView>) {
  21. uiView.netBasalPercent = netBasalPercent
  22. }
  23. }
  24. public final class BasalStateView: UIView {
  25. var netBasalPercent: Double = 0 {
  26. didSet {
  27. animateToPath(drawPath())
  28. }
  29. }
  30. override public class var layerClass : AnyClass {
  31. return CAShapeLayer.self
  32. }
  33. private var shapeLayer: CAShapeLayer {
  34. return layer as! CAShapeLayer
  35. }
  36. override init(frame: CGRect) {
  37. super.init(frame: frame)
  38. shapeLayer.lineWidth = 2
  39. updateTintColor()
  40. }
  41. required public init?(coder aDecoder: NSCoder) {
  42. super.init(coder: aDecoder)
  43. shapeLayer.lineWidth = 2
  44. updateTintColor()
  45. }
  46. override public func layoutSubviews() {
  47. super.layoutSubviews()
  48. animateToPath(drawPath())
  49. }
  50. public override func tintColorDidChange() {
  51. super.tintColorDidChange()
  52. updateTintColor()
  53. }
  54. private func updateTintColor() {
  55. shapeLayer.fillColor = tintColor.withAlphaComponent(0.5).cgColor
  56. shapeLayer.strokeColor = tintColor.cgColor
  57. }
  58. private func drawPath() -> CGPath {
  59. let startX = bounds.minX
  60. let endX = bounds.maxX
  61. let midY = bounds.midY
  62. let path = UIBezierPath()
  63. path.move(to: CGPoint(x: startX, y: midY))
  64. let leftAnchor = startX + 1/6 * bounds.size.width
  65. let rightAnchor = startX + 5/6 * bounds.size.width
  66. let yAnchor = bounds.midY - CGFloat(netBasalPercent) * (bounds.size.height - shapeLayer.lineWidth) / 2
  67. path.addLine(to: CGPoint(x: leftAnchor, y: midY))
  68. path.addLine(to: CGPoint(x: leftAnchor, y: yAnchor))
  69. path.addLine(to: CGPoint(x: rightAnchor, y: yAnchor))
  70. path.addLine(to: CGPoint(x: rightAnchor, y: midY))
  71. path.addLine(to: CGPoint(x: endX, y: midY))
  72. return path.cgPath
  73. }
  74. private static let animationKey = "com.loudnate.Naterade.shapePathAnimation"
  75. private func animateToPath(_ path: CGPath) {
  76. // Do not animate first draw
  77. if shapeLayer.path != nil {
  78. let animation = CABasicAnimation(keyPath: "path")
  79. animation.fromValue = shapeLayer.path ?? drawPath()
  80. animation.toValue = path
  81. animation.duration = 1
  82. animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
  83. shapeLayer.add(animation, forKey: type(of: self).animationKey)
  84. }
  85. // Do not draw when size is zero
  86. if bounds != .zero {
  87. shapeLayer.path = path
  88. }
  89. }
  90. }
  91. struct BasalStateSwiftUIViewPreviewWrapper: View {
  92. @State private var percent: Double = 1
  93. var body: some View {
  94. VStack(spacing: 20) {
  95. BasalStateSwiftUIView(netBasalPercent: percent).frame(width: 100, height: 100, alignment: .center)
  96. Button(action: {
  97. self.percent = self.percent * -1
  98. }) {
  99. Text("Toggle sign")
  100. }
  101. Text("Percent = \(percent)")
  102. }
  103. }
  104. }
  105. struct BasalStateSwiftUIViewPreview: PreviewProvider {
  106. static var previews: some View {
  107. BasalStateSwiftUIViewPreviewWrapper()
  108. }
  109. }