| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- //
- // BasalStateView.swift
- // Naterade
- //
- // Created by Nathan Racklyeft on 5/12/16.
- // Copyright © 2016 Nathan Racklyeft. All rights reserved.
- //
- import UIKit
- import SwiftUI
- public struct BasalStateSwiftUIView: UIViewRepresentable {
- var netBasalPercent: Double
-
- public init(netBasalPercent: Double) {
- self.netBasalPercent = netBasalPercent
- }
-
- public func makeUIView(context: UIViewRepresentableContext<BasalStateSwiftUIView>) -> BasalStateView {
- let view = BasalStateView()
- view.netBasalPercent = netBasalPercent
- return view
- }
- public func updateUIView(_ uiView: BasalStateView, context: UIViewRepresentableContext<BasalStateSwiftUIView>) {
- uiView.netBasalPercent = netBasalPercent
- }
- }
- public final class BasalStateView: UIView {
-
- var netBasalPercent: Double = 0 {
- didSet {
- animateToPath(drawPath())
- }
- }
- override public class var layerClass : AnyClass {
- return CAShapeLayer.self
- }
- private var shapeLayer: CAShapeLayer {
- return layer as! CAShapeLayer
- }
- override init(frame: CGRect) {
- super.init(frame: frame)
- shapeLayer.lineWidth = 2
- updateTintColor()
- }
- required public init?(coder aDecoder: NSCoder) {
- super.init(coder: aDecoder)
- shapeLayer.lineWidth = 2
- updateTintColor()
- }
- override public func layoutSubviews() {
- super.layoutSubviews()
- animateToPath(drawPath())
- }
- public override func tintColorDidChange() {
- super.tintColorDidChange()
- updateTintColor()
- }
- private func updateTintColor() {
- shapeLayer.fillColor = tintColor.withAlphaComponent(0.5).cgColor
- shapeLayer.strokeColor = tintColor.cgColor
- }
- private func drawPath() -> CGPath {
- let startX = bounds.minX
- let endX = bounds.maxX
- let midY = bounds.midY
- let path = UIBezierPath()
- path.move(to: CGPoint(x: startX, y: midY))
- let leftAnchor = startX + 1/6 * bounds.size.width
- let rightAnchor = startX + 5/6 * bounds.size.width
- let yAnchor = bounds.midY - CGFloat(netBasalPercent) * (bounds.size.height - shapeLayer.lineWidth) / 2
- path.addLine(to: CGPoint(x: leftAnchor, y: midY))
- path.addLine(to: CGPoint(x: leftAnchor, y: yAnchor))
- path.addLine(to: CGPoint(x: rightAnchor, y: yAnchor))
- path.addLine(to: CGPoint(x: rightAnchor, y: midY))
- path.addLine(to: CGPoint(x: endX, y: midY))
- return path.cgPath
- }
- private static let animationKey = "com.loudnate.Naterade.shapePathAnimation"
- private func animateToPath(_ path: CGPath) {
- // Do not animate first draw
- if shapeLayer.path != nil {
- let animation = CABasicAnimation(keyPath: "path")
- animation.fromValue = shapeLayer.path ?? drawPath()
- animation.toValue = path
- animation.duration = 1
- animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
- shapeLayer.add(animation, forKey: type(of: self).animationKey)
- }
- // Do not draw when size is zero
- if bounds != .zero {
- shapeLayer.path = path
- }
- }
- }
- struct BasalStateSwiftUIViewPreviewWrapper: View {
- @State private var percent: Double = 1
-
- var body: some View {
- VStack(spacing: 20) {
- BasalStateSwiftUIView(netBasalPercent: percent).frame(width: 100, height: 100, alignment: .center)
- Button(action: {
- self.percent = self.percent * -1
- }) {
- Text("Toggle sign")
- }
- Text("Percent = \(percent)")
- }
- }
- }
- struct BasalStateSwiftUIViewPreview: PreviewProvider {
- static var previews: some View {
- BasalStateSwiftUIViewPreviewWrapper()
- }
- }
|