NAKPlaybackIndicatorView.m 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. //
  2. // NAKPlaybackIndicatorView.m
  3. // PlaybackIndicator
  4. //
  5. // Created by Yuji Nakayama on 1/27/14.
  6. // Copyright (c) 2014 Yuji Nakayama. All rights reserved.
  7. //
  8. #import "NAKPlaybackIndicatorView.h"
  9. #import "NAKPlaybackIndicatorContentView.h"
  10. @interface NAKPlaybackIndicatorView ()
  11. - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_DESIGNATED_INITIALIZER;
  12. @property (nonatomic, readonly, nonnull) NAKPlaybackIndicatorContentView* contentView;
  13. @property (nonatomic, assign) BOOL hasInstalledConstraints;
  14. @end
  15. @implementation NAKPlaybackIndicatorView
  16. #pragma mark - Initialization
  17. - (instancetype)initWithFrame:(CGRect)frame
  18. {
  19. return [self initWithFrame:frame style:[NAKPlaybackIndicatorViewStyle defaultStyle]];
  20. }
  21. - (instancetype)initWithStyle:(NAKPlaybackIndicatorViewStyle*)style
  22. {
  23. return [self initWithFrame:CGRectZero style:style];
  24. }
  25. - (instancetype)initWithFrame:(CGRect)frame style:(NAKPlaybackIndicatorViewStyle*)style
  26. {
  27. self = [super initWithFrame:frame];
  28. if (self) {
  29. [self commonInitWithStyle:style];
  30. }
  31. return self;
  32. }
  33. - (instancetype)initWithCoder:(NSCoder*)aDecoder
  34. {
  35. self = [super initWithCoder:aDecoder];
  36. if (self) {
  37. [self commonInitWithStyle:[NAKPlaybackIndicatorViewStyle defaultStyle]];
  38. }
  39. return self;
  40. }
  41. - (void)commonInitWithStyle:(NAKPlaybackIndicatorViewStyle*)style
  42. {
  43. self.layer.masksToBounds = YES;
  44. _contentView = [[NAKPlaybackIndicatorContentView alloc] initWithStyle:style];
  45. [self addSubview:_contentView];
  46. [self prepareLayoutPriorities];
  47. [self setNeedsUpdateConstraints];
  48. self.state = NAKPlaybackIndicatorViewStateStopped;
  49. self.hidesWhenStopped = YES;
  50. [[NSNotificationCenter defaultCenter] addObserver:self
  51. selector:@selector(applicationWillEnterForeground:)
  52. name:UIApplicationWillEnterForegroundNotification
  53. object:nil];
  54. }
  55. - (void)prepareLayoutPriorities
  56. {
  57. // Custom views should set default values for both orientations on creation,
  58. // based on their content, typically to NSLayoutPriorityDefaultLow or NSLayoutPriorityDefaultHigh.
  59. [self setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
  60. [self setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
  61. [self setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
  62. [self setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
  63. }
  64. - (void)dealloc
  65. {
  66. [[NSNotificationCenter defaultCenter] removeObserver:self];
  67. }
  68. #pragma mark - Auto Layout
  69. - (void)updateConstraints
  70. {
  71. if (!self.hasInstalledConstraints) {
  72. [self addConstraint:[NSLayoutConstraint constraintWithItem:self
  73. attribute:NSLayoutAttributeCenterX
  74. relatedBy:NSLayoutRelationEqual
  75. toItem:self.contentView
  76. attribute:NSLayoutAttributeCenterX
  77. multiplier:1.0
  78. constant:0.0]];
  79. [self addConstraint:[NSLayoutConstraint constraintWithItem:self
  80. attribute:NSLayoutAttributeCenterY
  81. relatedBy:NSLayoutRelationEqual
  82. toItem:self.contentView
  83. attribute:NSLayoutAttributeCenterY
  84. multiplier:1.0
  85. constant:0.0]];
  86. self.hasInstalledConstraints = YES;
  87. }
  88. [super updateConstraints];
  89. }
  90. - (CGSize)intrinsicContentSize
  91. {
  92. return [self.contentView intrinsicContentSize];
  93. }
  94. // iOS 9 or later
  95. - (UIView*)viewForLastBaselineLayout
  96. {
  97. return self.contentView;
  98. }
  99. // iOS 8
  100. - (UIView*)viewForBaselineLayout
  101. {
  102. return self.contentView;
  103. }
  104. #pragma mark - Frame-Based Layout
  105. - (CGSize)sizeThatFits:(CGSize)size
  106. {
  107. return [self intrinsicContentSize];
  108. }
  109. #pragma mark - Properties
  110. - (void)setHidesWhenStopped:(BOOL)hidesWhenStopped
  111. {
  112. _hidesWhenStopped = hidesWhenStopped;
  113. if (self.state == NAKPlaybackIndicatorViewStateStopped) {
  114. self.hidden = self.hidesWhenStopped;
  115. }
  116. }
  117. - (void)setState:(NAKPlaybackIndicatorViewState)state
  118. {
  119. _state = state;
  120. if (self.state == NAKPlaybackIndicatorViewStateStopped) {
  121. [self stopAnimating];
  122. if (self.hidesWhenStopped) {
  123. self.hidden = YES;
  124. }
  125. } else {
  126. if (self.state == NAKPlaybackIndicatorViewStatePlaying) {
  127. [self startAnimating];
  128. } else if (self.state == NAKPlaybackIndicatorViewStatePaused) {
  129. [self stopAnimating];
  130. }
  131. self.hidden = NO;
  132. }
  133. }
  134. #pragma mark - Helpers
  135. - (void)startAnimating
  136. {
  137. if (self.contentView.isOscillating) {
  138. return;
  139. }
  140. [self.contentView stopDecay];
  141. [self.contentView startOscillation];
  142. }
  143. - (void)stopAnimating
  144. {
  145. if (!self.contentView.isOscillating) {
  146. return;
  147. }
  148. [self.contentView stopOscillation];
  149. [self.contentView startDecay];
  150. }
  151. #pragma mark - Notification
  152. - (void)applicationWillEnterForeground:(NSNotification*)notification
  153. {
  154. // When an app entered background, UIKit removes all animations
  155. // even if it's an infinite animation.
  156. // So we restart the animation here if it should be when the app came back to foreground.
  157. if (self.state == NAKPlaybackIndicatorViewStatePlaying) {
  158. [self startAnimating];
  159. }
  160. }
  161. @end