LevelMaskView.swift 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. //
  2. // LevelMaskView.swift
  3. // Loop
  4. //
  5. // Created by Nate Racklyeft on 8/28/16.
  6. // Copyright © 2016 Nathan Racklyeft. All rights reserved.
  7. //
  8. import UIKit
  9. // Displays a variable-height level indicator, masked by an image.
  10. // Inspired by https://github.com/carekit-apple/CareKit/blob/master/CareKit/CareCard/OCKHeartView.h
  11. public class LevelMaskView: UIView {
  12. var firstDataUpdate = true
  13. var value: Double = 1.0 {
  14. didSet {
  15. animateFill(duration: firstDataUpdate ? 0 : 1.25)
  16. firstDataUpdate = false
  17. }
  18. }
  19. private var clampedValue: Double {
  20. return value.clamped(to: 0...1.0)
  21. }
  22. @IBInspectable var maskImage: UIImage? {
  23. didSet {
  24. fillView?.removeFromSuperview()
  25. mask?.removeFromSuperview()
  26. maskImageView?.removeFromSuperview()
  27. guard let maskImage = maskImage else {
  28. fillView = nil
  29. mask = nil
  30. maskImageView = nil
  31. return
  32. }
  33. mask = UIView()
  34. maskImageView = UIImageView(image: maskImage)
  35. maskImageView!.frame = CGRect(origin: .zero, size: frame.size)
  36. maskImageView!.contentMode = .scaleAspectFit
  37. mask!.addSubview(maskImageView!)
  38. clipsToBounds = true
  39. fillView = UIView()
  40. fillView!.backgroundColor = tintColor
  41. addSubview(fillView!)
  42. }
  43. }
  44. private var fillView: UIView?
  45. private var maskImageView: UIView?
  46. override public func layoutSubviews() {
  47. super.layoutSubviews()
  48. guard let maskImageView = maskImageView else { return }
  49. let maskImageViewSize = maskImageView.frame.size
  50. mask?.frame = CGRect(origin: .zero, size: maskImageViewSize)
  51. mask?.center = CGPoint(x: bounds.midX, y: bounds.midY)
  52. self.maskImageView?.frame = mask?.bounds ?? bounds
  53. if (fillView?.layer.animationKeys()?.count ?? 0) == 0 {
  54. updateFillViewFrame()
  55. }
  56. }
  57. override public func tintColorDidChange() {
  58. super.tintColorDidChange()
  59. fillView?.backgroundColor = tintColor
  60. }
  61. private func animateFill(duration: TimeInterval) {
  62. UIView.animate(withDuration: duration, delay: 0, options: .beginFromCurrentState, animations: {
  63. self.updateFillViewFrame()
  64. }, completion: nil)
  65. }
  66. private func updateFillViewFrame() {
  67. guard let maskViewFrame = mask?.frame else { return }
  68. var fillViewFrame = maskViewFrame
  69. fillViewFrame.origin.y = maskViewFrame.maxY
  70. fillViewFrame.size.height = -CGFloat(clampedValue) * maskViewFrame.height
  71. fillView?.frame = fillViewFrame
  72. }
  73. }