LevelMaskView.swift 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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!.contentMode = .center
  36. mask!.addSubview(maskImageView!)
  37. clipsToBounds = true
  38. fillView = UIView()
  39. fillView!.backgroundColor = tintColor
  40. addSubview(fillView!)
  41. }
  42. }
  43. private var fillView: UIView?
  44. private var maskImageView: UIView?
  45. override public func layoutSubviews() {
  46. super.layoutSubviews()
  47. guard let maskImage = maskImage else { return }
  48. let maskImageSize = maskImage.size
  49. mask?.frame = CGRect(origin: .zero, size: maskImageSize)
  50. mask?.center = CGPoint(x: bounds.midX, y: bounds.midY)
  51. maskImageView?.frame = mask?.bounds ?? bounds
  52. if (fillView?.layer.animationKeys()?.count ?? 0) == 0 {
  53. updateFillViewFrame()
  54. }
  55. }
  56. override public func tintColorDidChange() {
  57. super.tintColorDidChange()
  58. fillView?.backgroundColor = tintColor
  59. }
  60. private func animateFill(duration: TimeInterval) {
  61. UIView.animate(withDuration: duration, delay: 0, options: .beginFromCurrentState, animations: {
  62. self.updateFillViewFrame()
  63. }, completion: nil)
  64. }
  65. private func updateFillViewFrame() {
  66. guard let maskViewFrame = mask?.frame else { return }
  67. var fillViewFrame = maskViewFrame
  68. fillViewFrame.origin.y = maskViewFrame.maxY
  69. fillViewFrame.size.height = -CGFloat(clampedValue) * maskViewFrame.height
  70. fillView?.frame = fillViewFrame
  71. }
  72. }