123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- //
- // LOTAnimatedSwitch.m
- // Lottie
- //
- // Created by brandon_withrow on 8/25/17.
- // Copyright © 2017 Airbnb. All rights reserved.
- //
- #import "LOTAnimatedSwitch.h"
- #import "LOTAnimationView.h"
- #import "CGGeometry+LOTAdditions.h"
- @implementation LOTAnimatedSwitch {
- CGFloat _onStartProgress;
- CGFloat _onEndProgress;
- CGFloat _offStartProgress;
- CGFloat _offEndProgress;
- CGPoint _touchTrackingStart;
- BOOL _on;
- BOOL _suppressToggle;
- BOOL _toggleToState;
- }
- /// Convenience method to initialize a control from the Main Bundle by name
- + (instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName {
- return [self switchNamed:toggleName inBundle:[NSBundle mainBundle]];
- }
- /// Convenience method to initialize a control from the specified bundle by name
- + (instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName inBundle:(NSBundle * _Nonnull)bundle {
- LOTComposition *composition = [LOTComposition animationNamed:toggleName inBundle:bundle];
- LOTAnimatedSwitch *animatedControl = [[self alloc] initWithFrame:CGRectZero];
- if (composition) {
- [animatedControl setAnimationComp:composition];
- animatedControl.bounds = composition.compBounds;
- }
- return animatedControl;
- }
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- self.accessibilityHint = NSLocalizedString(@"Double tap to toggle setting.", @"Double tap to toggle setting.");
- _onStartProgress = 0;
- _onEndProgress = 1;
- _offStartProgress = 1;
- _offEndProgress = 0;
- _on = NO;
- [self addTarget:self action:@selector(_toggle) forControlEvents:UIControlEventTouchUpInside];
- }
- return self;
- }
- - (void)setAnimationComp:(LOTComposition *)animationComp {
- [super setAnimationComp:animationComp];
- [self setOn:_on animated:NO];
- }
- #pragma mark - External Methods
- - (void)setProgressRangeForOnState:(CGFloat)fromProgress toProgress:(CGFloat)toProgress {
- _onStartProgress = fromProgress;
- _onEndProgress = toProgress;
- [self setOn:_on animated:NO];
- }
- - (void)setProgressRangeForOffState:(CGFloat)fromProgress toProgress:(CGFloat)toProgress {
- _offStartProgress = fromProgress;
- _offEndProgress = toProgress;
- [self setOn:_on animated:NO];
- }
- - (void)setOn:(BOOL)on {
- [self setOn:on animated:NO];
- }
- - (void)setOn:(BOOL)on animated:(BOOL)animated {
- _on = on;
-
- CGFloat startProgress = on ? _onStartProgress : _offStartProgress;
- CGFloat endProgress = on ? _onEndProgress : _offEndProgress;
- CGFloat finalProgress = endProgress;
- if (self.animationView.animationProgress < MIN(startProgress, endProgress) ||
- self.animationView.animationProgress > MAX(startProgress, endProgress)) {
- if (self.animationView.animationProgress != (!_on ? _onEndProgress : _offEndProgress)) {
- // Current progress is in the wrong timeline. Switch.
- endProgress = on ? _offStartProgress : _onStartProgress;
- startProgress = on ? _offEndProgress : _onEndProgress;
- }
- }
-
- if (finalProgress == self.animationView.animationProgress) {
- return;
- }
-
- if (animated) {
- [self.animationView pause];
- [self.animationView playFromProgress:startProgress toProgress:endProgress withCompletion:^(BOOL animationFinished) {
- if (animationFinished) {
- self.animationView.animationProgress = finalProgress;
- }
- }];
- } else {
- self.animationView.animationProgress = endProgress;
- }
- }
- - (NSString *)accessibilityValue {
- return self.isOn ? NSLocalizedString(@"On", @"On") : NSLocalizedString(@"Off", @"Off");
- }
- #pragma mark - Internal Methods
- - (void)_toggle {
- if (!_suppressToggle) {
- [self _toggleAndSendActions];
- }
- }
- - (void)_toggleAndSendActions {
- if (self.isEnabled) {
- #ifndef TARGET_OS_TV
- if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
- UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
- [generator impactOccurred];
- }
- #endif
- [self setOn:!_on animated:YES];
- [self sendActionsForControlEvents:UIControlEventValueChanged];
- }
- }
- - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
- [super beginTrackingWithTouch:touch withEvent:event];
- _suppressToggle = NO;
- _touchTrackingStart = [touch locationInView:self];
- return YES;
- }
- - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
- BOOL superContinue = [super continueTrackingWithTouch:touch withEvent:event];
- if (!_interactiveGesture) {
- return superContinue;
- }
- CGPoint location = [touch locationInView:self];
- CGFloat diff = location.x - _touchTrackingStart.x;
- if (LOT_PointDistanceFromPoint(_touchTrackingStart, location) > self.bounds.size.width * 0.25) {
- // The touch has moved enough to register as its own gesture. Suppress the touch up toggle.
- _suppressToggle = YES;
- }
- #ifdef __IPHONE_11_0
- // Xcode 9+
- if (@available(iOS 9.0, *)) {
- #else
- // Xcode 8-
- if ([UIView respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute:)]) {
- #endif
- if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
- diff = diff * -1;
- }
- }
- if (_on) {
- diff = diff * -1;
- if (diff <= 0) {
- self.animationView.animationProgress = _onEndProgress;
- _toggleToState = YES;
- } else {
- diff = MAX(MIN(self.bounds.size.width, diff), 0);
- self.animationView.animationProgress = LOT_RemapValue(diff, 0, self.bounds.size.width, _offStartProgress, _offEndProgress);
- _toggleToState = (diff / self.bounds.size.width) > 0.5 ? NO : YES;
- }
- } else {
- if (diff <= 0) {
- self.animationView.animationProgress = _offEndProgress;
- _toggleToState = NO;
- } else {
- diff = MAX(MIN(self.bounds.size.width, diff), 0);
- self.animationView.animationProgress = LOT_RemapValue(diff, 0, self.bounds.size.width, _onStartProgress, _onEndProgress);
- _toggleToState = (diff / self.bounds.size.width) > 0.5 ? YES : NO;
- }
- }
- return YES;
- }
- - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
- [super endTrackingWithTouch:touch withEvent:event];
- if (!_interactiveGesture) {
- return;
- }
- if (_suppressToggle) {
- if (_toggleToState != _on) {
- [self _toggleAndSendActions];
- } else {
- [self setOn:_toggleToState animated:YES];
- }
- }
- }
- @end
|