From 9f6916b8baedb328c74203b9e3862c10e001249a Mon Sep 17 00:00:00 2001 From: Charles Powell Date: Sun, 11 Jun 2017 14:35:06 -0700 Subject: [PATCH] Add MLLeft and MLRight types to Objective-C [ObjC] --- Sources/ObjC/MarqueeLabel.h | 6 ++- Sources/ObjC/MarqueeLabel.m | 95 ++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/Sources/ObjC/MarqueeLabel.h b/Sources/ObjC/MarqueeLabel.h index aa870618..8874394d 100755 --- a/Sources/ObjC/MarqueeLabel.h +++ b/Sources/ObjC/MarqueeLabel.h @@ -17,7 +17,11 @@ typedef NS_ENUM(NSUInteger, MarqueeType) { /** Continuously scrolls left (with a pause at the original position if animationDelay is set). See the `trailingBuffer` property to define a spacing between the repeating strings.*/ MLContinuous = 2, /** Continuously scrolls right (with a pause at the original position if animationDelay is set). See the `trailingBuffer` property to define a spacing between the repeating strings.*/ - MLContinuousReverse = 3 + MLContinuousReverse = 3, + /** Scrolls left first, then does not return to the original position. */ + MLLeft = 4, + /** Scrolls right first, then does not return to the original position. */ + MLRight = 5 }; diff --git a/Sources/ObjC/MarqueeLabel.m b/Sources/ObjC/MarqueeLabel.m index 315af8a5..c4a161a4 100755 --- a/Sources/ObjC/MarqueeLabel.m +++ b/Sources/ObjC/MarqueeLabel.m @@ -21,6 +21,9 @@ // iOS Version check for iOS 8.0.0 #define SYSTEM_VERSION_IS_8_0_X ([[[UIDevice currentDevice] systemVersion] hasPrefix:@"8.0"]) +// Define "a long time" for MLLeft and MLRight types +#define CGFLOAT_LONG_DURATION 60*60*24*365 // One year in seconds + // Helpers @interface GradientSetupAnimation : CABasicAnimation @end @@ -332,6 +335,7 @@ - (void)updateSublabelAndBeginScroll:(BOOL)beginScroll { switch (self.marqueeType) { case MLContinuousReverse: case MLRightLeft: + case MLRight: CGRectDivide(self.bounds, &unusedFrame, &labelFrame, self.leadingBuffer, CGRectMaxXEdge); labelFrame = CGRectIntegral(labelFrame); break; @@ -388,6 +392,7 @@ - (void)updateSublabelAndBeginScroll:(BOOL)beginScroll { } case MLRightLeft: + case MLRight: { self.homeLabelFrame = CGRectIntegral(CGRectMake(self.bounds.size.width - (expectedLabelSize.width + self.leadingBuffer), 0.0f, expectedLabelSize.width, self.bounds.size.height)); self.awayOffset = (expectedLabelSize.width + self.trailingBuffer + self.leadingBuffer) - self.bounds.size.width; @@ -408,6 +413,7 @@ - (void)updateSublabelAndBeginScroll:(BOOL)beginScroll { } case MLLeftRight: + case MLLeft: { self.homeLabelFrame = CGRectIntegral(CGRectMake(self.leadingBuffer, 0.0f, expectedLabelSize.width, self.bounds.size.height)); self.awayOffset = self.bounds.size.width - (expectedLabelSize.width + self.leadingBuffer + self.trailingBuffer); @@ -513,6 +519,10 @@ - (void)beginScrollWithDelay:(BOOL)delay { case MLContinuousReverse: [self scrollContinuousWithInterval:self.animationDuration after:(delay ? self.animationDelay : 0.0)]; break; + case MLLeft: + case MLRight: + [self scrollAwayWithInterval:self.animationDuration delayAmount:(delay ? self.animationDelay : 0.0) shouldReturn:NO]; + break; default: [self scrollAwayWithInterval:self.animationDuration]; break; @@ -535,10 +545,10 @@ - (void)scrollAwayWithInterval:(NSTimeInterval)interval { } - (void)scrollAwayWithInterval:(NSTimeInterval)interval delay:(BOOL)delay { - [self scrollAwayWithInterval:interval delayAmount:(delay ? self.animationDelay : 0.0)]; + [self scrollAwayWithInterval:interval delayAmount:(delay ? self.animationDelay : 0.0) shouldReturn:YES]; } -- (void)scrollAwayWithInterval:(NSTimeInterval)interval delayAmount:(NSTimeInterval)delayAmount { +- (void)scrollAwayWithInterval:(NSTimeInterval)interval delayAmount:(NSTimeInterval)delayAmount shouldReturn:(BOOL)shouldReturn { // Check for conditions which would prevent scrolling if (![self labelReadyForScroll]) { return; @@ -554,7 +564,7 @@ - (void)scrollAwayWithInterval:(NSTimeInterval)interval delayAmount:(NSTimeInter [CATransaction begin]; // Set Duration - [CATransaction setAnimationDuration:(2.0 * (delayAmount + interval))]; + [CATransaction setAnimationDuration:(!shouldReturn ? CGFLOAT_MAX : 2.0 * (delayAmount + interval))]; // Create animation for gradient, if needed if (self.fadeLength != 0.0f) { @@ -580,7 +590,7 @@ - (void)scrollAwayWithInterval:(NSTimeInterval)interval delayAmount:(NSTimeInter if (self.window && ![weakSelf.subLabel.layer animationForKey:@"position"]) { // Begin again, if conditions met if (weakSelf.labelShouldScroll && !weakSelf.tapToScroll && !weakSelf.holdScrolling) { - [weakSelf scrollAwayWithInterval:interval delayAmount:delayAmount]; + [weakSelf scrollAwayWithInterval:interval delayAmount:delayAmount shouldReturn:shouldReturn]; } } }; @@ -589,11 +599,25 @@ - (void)scrollAwayWithInterval:(NSTimeInterval)interval delayAmount:(NSTimeInter // Create animation for position CGPoint homeOrigin = self.homeLabelFrame.origin; CGPoint awayOrigin = MLOffsetCGPoint(self.homeLabelFrame.origin, self.awayOffset); - NSArray *values = @[[NSValue valueWithCGPoint:homeOrigin], // Initial location, home - [NSValue valueWithCGPoint:homeOrigin], // Initial delay, at home - [NSValue valueWithCGPoint:awayOrigin], // Animation to away - [NSValue valueWithCGPoint:awayOrigin], // Delay at away - [NSValue valueWithCGPoint:homeOrigin]]; // Animation to home + + NSArray *values = nil; + switch (self.marqueeType) { + case MLLeft: + case MLRight: + values = @[[NSValue valueWithCGPoint:homeOrigin], // Initial location, home + [NSValue valueWithCGPoint:homeOrigin], // Initial delay, at home + [NSValue valueWithCGPoint:awayOrigin], // Animation to away + [NSValue valueWithCGPoint:awayOrigin]]; // Delay at away + break; + + default: + values = @[[NSValue valueWithCGPoint:homeOrigin], // Initial location, home + [NSValue valueWithCGPoint:homeOrigin], // Initial delay, at home + [NSValue valueWithCGPoint:awayOrigin], // Animation to away + [NSValue valueWithCGPoint:awayOrigin], // Delay at away + [NSValue valueWithCGPoint:homeOrigin]]; // Animation to home + break; + } CAKeyframeAnimation *awayAnim = [self keyFrameAnimationForProperty:@"position" values:values @@ -741,6 +765,7 @@ - (void)applyGradientMaskForFadeLength:(CGFloat)fadeLength animated:(BOOL)animat switch (self.marqueeType) { case MLContinuousReverse: case MLRightLeft: + case MLRight: adjustedColors = @[(trailingFadeNeeded ? transparent : opaque), opaque, opaque, @@ -807,8 +832,7 @@ - (CAKeyframeAnimation *)keyFrameAnimationForGradientFadeLength:(CGFloat)fadeLen case MLRightLeft: // Calculate total animation duration totalDuration = 2.0 * (delayAmount + interval); - keyTimes = @[ - @(0.0), // 1) Initial gradient + keyTimes = @[@(0.0), // 1) Initial gradient @(delayAmount/totalDuration), // 2) Begin of LE fade-in, just as scroll away starts @((delayAmount + 0.4)/totalDuration), // 3) End of LE fade in [LE fully faded] @((delayAmount + interval - 0.4)/totalDuration), // 4) Begin of TE fade out, just before scroll away finishes @@ -819,6 +843,17 @@ - (CAKeyframeAnimation *)keyFrameAnimationForGradientFadeLength:(CGFloat)fadeLen @(1.0)]; // 9) End of LE fade out, just as scroll home finishes break; + case MLLeft: + case MLRight: + // Calculate total animation duration + totalDuration = CGFLOAT_MAX; + keyTimes = @[@(0.0), // 1) Initial gradient + @(delayAmount/totalDuration), // 2) Begin of LE fade-in, just as scroll away starts + @((delayAmount + 0.4)/totalDuration), // 3) End of LE fade in [LE fully faded] + @((delayAmount + interval - 0.4)/totalDuration), // 4) Begin of TE fade out, just before scroll away finishes + @((delayAmount + interval)/totalDuration), // 5) End of TE fade out [TE fade removed] + @(1.0)]; + break; case MLContinuousReverse: default: // Calculate total animation duration @@ -858,6 +893,17 @@ - (CAKeyframeAnimation *)keyFrameAnimationForGradientFadeLength:(CGFloat)fadeLen ]; break; + case MLRight: + values = @[ + (currentValues ? currentValues : @[transp, opaque, opaque, opaque]), // 1) + @[transp, opaque, opaque, opaque], // 2) + @[transp, opaque, opaque, transp], // 3) + @[transp, opaque, opaque, transp], // 4) + @[opaque, opaque, opaque, transp], // 5) + @[opaque, opaque, opaque, transp], // 6) + ]; + break; + case MLRightLeft: values = @[ (currentValues ? currentValues : @[transp, opaque, opaque, opaque]), // 1) @@ -883,6 +929,17 @@ - (CAKeyframeAnimation *)keyFrameAnimationForGradientFadeLength:(CGFloat)fadeLen ]; break; + case MLLeft: + values = @[ + (currentValues ? currentValues : @[opaque, opaque, opaque, transp]), // 1) + @[opaque, opaque, opaque, transp], // 2) + @[transp, opaque, opaque, transp], // 3) + @[transp, opaque, opaque, transp], // 4) + @[transp, opaque, opaque, opaque], // 5) + @[transp, opaque, opaque, opaque], // 6) + ]; + break; + case MLLeftRight: default: values = @[ @@ -938,6 +995,22 @@ - (CAKeyframeAnimation *)keyFrameAnimationForProperty:(NSString *)property break; + case MLLeft: + case MLRight: + NSAssert(values.count == 4, @"Incorrect number of values passed for MLLeft-type animation"); + totalDuration = CGFLOAT_MAX; + // Set up keyTimes + animation.keyTimes = @[@(0.0), // Initial location, home + @(delayAmount/totalDuration), // Initial delay, at home + @((delayAmount + interval)/totalDuration), // Animation to away + @(1.0)]; // Animation to home + + animation.timingFunctions = @[timingFunction, + timingFunction, + timingFunction]; + + break; + // MLContinuous // MLContinuousReverse default: