123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776 |
- //
- // MarqueeLabel.m
- //
- // Created by Charles Powell on 1/31/11.
- // Copyright (c) 2011-2015 Charles Powell. All rights reserved.
- //
- #import "MarqueeLabel.h"
- #import <QuartzCore/QuartzCore.h>
- // Notification strings
- NSString *const kMarqueeLabelControllerRestartNotification = @"MarqueeLabelViewControllerRestart";
- NSString *const kMarqueeLabelShouldLabelizeNotification = @"MarqueeLabelShouldLabelizeNotification";
- NSString *const kMarqueeLabelShouldAnimateNotification = @"MarqueeLabelShouldAnimateNotification";
- NSString *const kMarqueeLabelAnimationCompletionBlock = @"MarqueeLabelAnimationCompletionBlock";
- // Animation completion block
- typedef void(^MLAnimationCompletionBlock)(BOOL finished);
- // 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
- @interface UIView (MarqueeLabelHelpers)
- - (UIViewController *)firstAvailableViewController;
- - (id)traverseResponderChainForFirstViewController;
- @end
- @interface CAMediaTimingFunction (MarqueeLabelHelpers)
- - (NSArray *)controlPoints;
- - (CGFloat)durationPercentageForPositionPercentage:(CGFloat)positionPercentage withDuration:(NSTimeInterval)duration;
- @end
- @interface MarqueeLabel()
- @property (nonatomic, strong) UILabel *subLabel;
- @property (nonatomic, assign) NSTimeInterval animationDuration;
- @property (nonatomic, assign, readonly) BOOL labelShouldScroll;
- @property (nonatomic, weak) UITapGestureRecognizer *tapRecognizer;
- @property (nonatomic, assign) CGRect homeLabelFrame;
- @property (nonatomic, assign) CGFloat awayOffset;
- @property (nonatomic, assign, readwrite) BOOL isPaused;
- // Support
- @property (nonatomic, copy) MLAnimationCompletionBlock scrollCompletionBlock;
- @property (nonatomic, strong) NSArray *gradientColors;
- CGPoint MLOffsetCGPoint(CGPoint point, CGFloat offset);
- @end
- @implementation MarqueeLabel
- #pragma mark - Class Methods and handlers
- + (void)restartLabelsOfController:(UIViewController *)controller {
- [MarqueeLabel notifyController:controller
- withMessage:kMarqueeLabelControllerRestartNotification];
- }
- + (void)controllerViewWillAppear:(UIViewController *)controller {
- [MarqueeLabel restartLabelsOfController:controller];
- }
- + (void)controllerViewDidAppear:(UIViewController *)controller {
- [MarqueeLabel restartLabelsOfController:controller];
- }
- + (void)controllerViewAppearing:(UIViewController *)controller {
- [MarqueeLabel restartLabelsOfController:controller];
- }
- + (void)controllerLabelsShouldLabelize:(UIViewController *)controller {
- [MarqueeLabel notifyController:controller
- withMessage:kMarqueeLabelShouldLabelizeNotification];
- }
- + (void)controllerLabelsShouldAnimate:(UIViewController *)controller {
- [MarqueeLabel notifyController:controller
- withMessage:kMarqueeLabelShouldAnimateNotification];
- }
- + (void)notifyController:(UIViewController *)controller withMessage:(NSString *)message
- {
- if (controller && message) {
- [[NSNotificationCenter defaultCenter] postNotificationName:message
- object:nil
- userInfo:[NSDictionary dictionaryWithObject:controller
- forKey:@"controller"]];
- }
- }
- - (void)viewControllerShouldRestart:(NSNotification *)notification {
- UIViewController *controller = [[notification userInfo] objectForKey:@"controller"];
- if (controller == [self firstAvailableViewController]) {
- [self restartLabel];
- }
- }
- - (void)labelsShouldLabelize:(NSNotification *)notification {
- UIViewController *controller = [[notification userInfo] objectForKey:@"controller"];
- if (controller == [self firstAvailableViewController]) {
- self.labelize = YES;
- }
- }
- - (void)labelsShouldAnimate:(NSNotification *)notification {
- UIViewController *controller = [[notification userInfo] objectForKey:@"controller"];
- if (controller == [self firstAvailableViewController]) {
- self.labelize = NO;
- }
- }
- #pragma mark - Initialization and Label Config
- - (id)initWithFrame:(CGRect)frame {
- return [self initWithFrame:frame duration:7.0 andFadeLength:0.0];
- }
- - (id)initWithFrame:(CGRect)frame duration:(NSTimeInterval)aLengthOfScroll andFadeLength:(CGFloat)aFadeLength {
- self = [super initWithFrame:frame];
- if (self) {
- [self setupLabel];
-
- _scrollDuration = aLengthOfScroll;
- self.fadeLength = MIN(aFadeLength, frame.size.width/2);
- }
- return self;
- }
- - (id)initWithFrame:(CGRect)frame rate:(CGFloat)pixelsPerSec andFadeLength:(CGFloat)aFadeLength {
- self = [super initWithFrame:frame];
- if (self) {
- [self setupLabel];
-
- _rate = pixelsPerSec;
- self.fadeLength = MIN(aFadeLength, frame.size.width/2);
- }
- return self;
- }
- - (id)initWithCoder:(NSCoder *)aDecoder {
- self = [super initWithCoder:aDecoder];
- if (self) {
- [self setupLabel];
-
- if (self.scrollDuration == 0) {
- self.scrollDuration = 7.0;
- }
- }
- return self;
- }
- - (void)awakeFromNib {
- [super awakeFromNib];
- [self forwardPropertiesToSubLabel];
- }
- - (void)prepareForInterfaceBuilder {
- [super prepareForInterfaceBuilder];
- [self forwardPropertiesToSubLabel];
- }
- + (Class)layerClass {
- return [CAReplicatorLayer class];
- }
- - (CAReplicatorLayer *)repliLayer {
- return (CAReplicatorLayer *)self.layer;
- }
- - (CAGradientLayer *)maskLayer {
- return (CAGradientLayer *)self.layer.mask;
- }
- - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
- // Do NOT call super, to prevent UILabel superclass from drawing into context
- // Label drawing is handled by sublabel and CAReplicatorLayer layer class
-
- // Draw only background color
- CGContextSetFillColorWithColor(ctx, self.backgroundColor.CGColor);
- CGContextFillRect(ctx, layer.bounds);
- }
- - (void)forwardPropertiesToSubLabel {
- /*
- Note that this method is currently ONLY called from awakeFromNib, i.e. when
- text properties are set via a Storyboard. As the Storyboard/IB doesn't currently
- support attributed strings, there's no need to "forward" the super attributedString value.
- */
-
- // Since we're a UILabel, we actually do implement all of UILabel's properties.
- // We don't care about these values, we just want to forward them on to our sublabel.
- NSArray *properties = @[@"baselineAdjustment", @"enabled", @"highlighted", @"highlightedTextColor",
- @"minimumFontSize", @"textAlignment",
- @"userInteractionEnabled", @"adjustsFontSizeToFitWidth",
- @"lineBreakMode", @"numberOfLines", @"contentMode"];
-
- // Iterate through properties
- self.subLabel.text = super.text;
- self.subLabel.font = super.font;
- self.subLabel.textColor = super.textColor;
- self.subLabel.backgroundColor = (super.backgroundColor == nil ? [UIColor clearColor] : super.backgroundColor);
- self.subLabel.shadowColor = super.shadowColor;
- self.subLabel.shadowOffset = super.shadowOffset;
- for (NSString *property in properties) {
- id val = [super valueForKey:property];
- [self.subLabel setValue:val forKey:property];
- }
- }
- - (void)setupLabel {
-
- // Basic UILabel options override
- self.clipsToBounds = YES;
- self.numberOfLines = 1;
-
- // Create first sublabel
- self.subLabel = [[UILabel alloc] initWithFrame:self.bounds];
- self.subLabel.tag = 700;
- self.subLabel.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
-
- [self addSubview:self.subLabel];
-
- // Setup default values
- _marqueeType = MLContinuous;
- _awayOffset = 0.0f;
- _animationCurve = UIViewAnimationOptionCurveLinear;
- _labelize = NO;
- _holdScrolling = NO;
- _tapToScroll = NO;
- _isPaused = NO;
- _fadeLength = 0.0f;
- _animationDelay = 1.0;
- _animationDuration = 0.0f;
- _leadingBuffer = 0.0f;
- _trailingBuffer = 0.0f;
-
- // Add notification observers
- // Custom class notifications
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewControllerShouldRestart:) name:kMarqueeLabelControllerRestartNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(labelsShouldLabelize:) name:kMarqueeLabelShouldLabelizeNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(labelsShouldAnimate:) name:kMarqueeLabelShouldAnimateNotification object:nil];
-
- // UIApplication state notifications
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(restartLabel) name:UIApplicationDidBecomeActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shutdownLabel) name:UIApplicationDidEnterBackgroundNotification object:nil];
- }
- - (void)minimizeLabelFrameWithMaximumSize:(CGSize)maxSize adjustHeight:(BOOL)adjustHeight {
- if (self.subLabel.text != nil) {
- // Calculate text size
- if (CGSizeEqualToSize(maxSize, CGSizeZero)) {
- maxSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
- }
- CGSize minimumLabelSize = [self subLabelSize];
-
- // Adjust for fade length
- CGSize minimumSize = CGSizeMake(minimumLabelSize.width + (self.fadeLength * 2), minimumLabelSize.height);
-
- // Find minimum size of options
- minimumSize = CGSizeMake(MIN(minimumSize.width, maxSize.width), MIN(minimumSize.height, maxSize.height));
-
- // Apply to frame
- self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, minimumSize.width, (adjustHeight ? minimumSize.height : self.frame.size.height));
- }
- }
- -(void)didMoveToSuperview {
- [self updateSublabel];
- }
- #pragma mark - MarqueeLabel Heavy Lifting
- - (void)layoutSubviews
- {
- [super layoutSubviews];
-
- [self updateSublabel];
- }
- - (void)willMoveToWindow:(UIWindow *)newWindow {
- if (!newWindow) {
- [self shutdownLabel];
- }
- }
- - (void)didMoveToWindow {
- if (!self.window) {
- [self shutdownLabel];
- } else {
- [self updateSublabel];
- }
- }
- - (void)updateSublabel {
- [self updateSublabelAndBeginScroll:YES];
- }
- - (void)updateSublabelAndBeginScroll:(BOOL)beginScroll {
- if (!self.subLabel.text || !self.superview) {
- return;
- }
-
- // Calculate expected size
- CGSize expectedLabelSize = [self subLabelSize];
-
-
- // Invalidate intrinsic size
- [self invalidateIntrinsicContentSize];
-
- // Move to home
- [self returnLabelToOriginImmediately];
-
- // Configure gradient for the current condition
- [self applyGradientMaskForFadeLength:self.fadeLength animated:YES];
-
- // Check if label should scroll
- // Can be because: 1) text fits, or 2) labelization
- // The holdScrolling property does NOT affect this
- if (!self.labelShouldScroll) {
- // Set text alignment and break mode to act like normal label
- self.subLabel.textAlignment = [super textAlignment];
- self.subLabel.lineBreakMode = [super lineBreakMode];
-
- CGRect labelFrame, unusedFrame;
- switch (self.marqueeType) {
- case MLContinuousReverse:
- case MLRightLeft:
- case MLRight:
- CGRectDivide(self.bounds, &unusedFrame, &labelFrame, self.leadingBuffer, CGRectMaxXEdge);
- labelFrame = CGRectIntegral(labelFrame);
- break;
-
- default:
- labelFrame = CGRectIntegral(CGRectMake(self.leadingBuffer, 0.0f, self.bounds.size.width - self.leadingBuffer, self.bounds.size.height));
- break;
- }
-
- self.homeLabelFrame = labelFrame;
- self.awayOffset = 0.0f;
-
- // Remove an additional sublabels (for continuous types)
- self.repliLayer.instanceCount = 1;
-
- // Set sublabel frame calculated labelFrame
- self.subLabel.frame = labelFrame;
-
- // Remove fade, as by definition none is needed in this case
- [self removeGradientMask];
-
- return;
- }
-
- // Label DOES need to scroll
-
- [self.subLabel setLineBreakMode:NSLineBreakByClipping];
-
- // Spacing between primary and second sublabel must be at least equal to leadingBuffer, and at least equal to the fadeLength
- CGFloat minTrailing = MAX(MAX(self.leadingBuffer, self.trailingBuffer), self.fadeLength);
-
- switch (self.marqueeType) {
- case MLContinuous:
- case MLContinuousReverse:
- {
- if (self.marqueeType == MLContinuous) {
- self.homeLabelFrame = CGRectIntegral(CGRectMake(self.leadingBuffer, 0.0f, expectedLabelSize.width, self.bounds.size.height));
- self.awayOffset = -(self.homeLabelFrame.size.width + minTrailing);
- } else {
- self.homeLabelFrame = CGRectIntegral(CGRectMake(self.bounds.size.width - (expectedLabelSize.width + self.leadingBuffer), 0.0f, expectedLabelSize.width, self.bounds.size.height));
- self.awayOffset = (self.homeLabelFrame.size.width + minTrailing);
- }
-
- self.subLabel.frame = self.homeLabelFrame;
-
- // Configure replication
- self.repliLayer.instanceCount = 2;
- self.repliLayer.instanceTransform = CATransform3DMakeTranslation(-self.awayOffset, 0.0, 0.0);
-
- // Recompute the animation duration
- self.animationDuration = (self.rate != 0) ? ((NSTimeInterval) fabs(self.awayOffset) / self.rate) : (self.scrollDuration);
-
- break;
- }
-
- 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;
-
- // Calculate animation duration
- self.animationDuration = (self.rate != 0) ? (NSTimeInterval)fabs(self.awayOffset / self.rate) : (self.scrollDuration);
-
- // Set frame and text
- self.subLabel.frame = self.homeLabelFrame;
-
- // Remove any replication
- self.repliLayer.instanceCount = 1;
-
- // Enforce text alignment for this type
- self.subLabel.textAlignment = NSTextAlignmentRight;
-
- break;
- }
-
- 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);
-
- // Calculate animation duration
- self.animationDuration = (self.rate != 0) ? (NSTimeInterval)fabs(self.awayOffset / self.rate) : (self.scrollDuration);
-
- // Set frame
- self.subLabel.frame = self.homeLabelFrame;
-
- // Remove any replication
- self.repliLayer.instanceCount = 1;
-
- // Enforce text alignment for this type
- self.subLabel.textAlignment = NSTextAlignmentLeft;
-
- break;
- }
-
- default:
- {
- // Something strange has happened
- self.homeLabelFrame = CGRectZero;
- self.awayOffset = 0.0f;
-
- // Do not attempt to begin scroll
- return;
- break;
- }
-
- } //end of marqueeType switch
-
- if (!self.tapToScroll && !self.holdScrolling && beginScroll) {
- [self beginScroll];
- }
- }
- - (CGSize)subLabelSize {
- // Calculate expected size
- CGSize expectedLabelSize = CGSizeZero;
- CGSize maximumLabelSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
-
- // Get size of subLabel
- expectedLabelSize = [self.subLabel sizeThatFits:maximumLabelSize];
- #ifdef TARGET_OS_IOS
- // Sanitize width to 5461.0f (largest width a UILabel will draw on an iPhone 6S Plus)
- expectedLabelSize.width = MIN(expectedLabelSize.width, 5461.0f);
- #elif TARGET_OS_TV
- // Sanitize width to 16384.0 (largest width a UILabel will draw on tvOS)
- expectedLabelSize.width = MIN(expectedLabelSize.width, 16384.0f);
- #endif
- // Adjust to own height (make text baseline match normal label)
- expectedLabelSize.height = self.bounds.size.height;
-
- return expectedLabelSize;
- }
- - (CGSize)sizeThatFits:(CGSize)size {
- CGSize fitSize = [self.subLabel sizeThatFits:size];
- fitSize.width += self.leadingBuffer;
- return fitSize;
- }
- #pragma mark - Animation Handlers
- - (BOOL)labelShouldScroll {
- BOOL stringLength = ([self.subLabel.text length] > 0);
- if (!stringLength) {
- return NO;
- }
-
- BOOL labelTooLarge = ([self subLabelSize].width + self.leadingBuffer > self.bounds.size.width + FLT_EPSILON);
- BOOL animationHasDuration = (self.scrollDuration > 0.0f || self.rate > 0.0f);
- return (!self.labelize && labelTooLarge && animationHasDuration);
- }
- - (BOOL)labelReadyForScroll {
- // Check if we have a superview
- if (!self.superview) {
- return NO;
- }
-
- if (!self.window) {
- return NO;
- }
-
- // Check if our view controller is ready
- UIViewController *viewController = [self firstAvailableViewController];
- if (!viewController.isViewLoaded) {
- return NO;
- }
-
- return YES;
- }
- - (void)beginScroll {
- [self beginScrollWithDelay:YES];
- }
- - (void)beginScrollWithDelay:(BOOL)delay {
- switch (self.marqueeType) {
- case MLContinuous:
- 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;
- }
- }
- - (void)returnLabelToOriginImmediately {
- // Remove gradient animations
- [self.layer.mask removeAllAnimations];
-
- // Remove sublabel position animations
- [self.subLabel.layer removeAllAnimations];
-
- // Remove compeltion blocks
- self.scrollCompletionBlock = nil;
- }
- - (void)scrollAwayWithInterval:(NSTimeInterval)interval {
- [self scrollAwayWithInterval:interval delay:YES];
- }
- - (void)scrollAwayWithInterval:(NSTimeInterval)interval delay:(BOOL)delay {
- [self scrollAwayWithInterval:interval delayAmount:(delay ? self.animationDelay : 0.0) shouldReturn:YES];
- }
- - (void)scrollAwayWithInterval:(NSTimeInterval)interval delayAmount:(NSTimeInterval)delayAmount shouldReturn:(BOOL)shouldReturn {
- // Check for conditions which would prevent scrolling
- if (![self labelReadyForScroll]) {
- return;
- }
-
- // Return labels to home (cancel any animations)
- [self returnLabelToOriginImmediately];
-
- // Call pre-animation method
- [self labelWillBeginScroll];
-
- // Animate
- [CATransaction begin];
-
- // Set Duration
- [CATransaction setAnimationDuration:(!shouldReturn ? CGFLOAT_MAX : 2.0 * (delayAmount + interval))];
-
- // Create animation for gradient, if needed
- if (self.fadeLength != 0.0f) {
- CAKeyframeAnimation *gradAnim = [self keyFrameAnimationForGradientFadeLength:self.fadeLength
- interval:interval
- delay:delayAmount];
- [self.layer.mask addAnimation:gradAnim forKey:@"gradient"];
- }
-
- __weak __typeof__(self) weakSelf = self;
- self.scrollCompletionBlock = ^(BOOL finished) {
- if (!finished || !weakSelf) {
- // Do not continue into the next loop
- return;
- }
- // Call returned home method
- [weakSelf labelReturnedToHome:YES];
- // Check to ensure that:
- // 1) We don't double fire if an animation already exists
- // 2) The instance is still attached to a window - this completion block is called for
- // many reasons, including if the animation is removed due to the view being removed
- // from the UIWindow (typically when the view controller is no longer the "top" view)
- 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 shouldReturn:shouldReturn];
- }
- }
- };
-
-
- // Create animation for position
- CGPoint homeOrigin = self.homeLabelFrame.origin;
- CGPoint awayOrigin = MLOffsetCGPoint(self.homeLabelFrame.origin, self.awayOffset);
-
- 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
- interval:interval
- delay:delayAmount];
- // Add completion block
- [awayAnim setValue:@(YES) forKey:kMarqueeLabelAnimationCompletionBlock];
-
- // Add animation
- [self.subLabel.layer addAnimation:awayAnim forKey:@"position"];
-
- [CATransaction commit];
- }
- - (void)scrollContinuousWithInterval:(NSTimeInterval)interval after:(NSTimeInterval)delayAmount {
- [self scrollContinuousWithInterval:interval after:delayAmount labelAnimation:nil gradientAnimation:nil];
- }
- - (void)scrollContinuousWithInterval:(NSTimeInterval)interval
- after:(NSTimeInterval)delayAmount
- labelAnimation:(CAKeyframeAnimation *)labelAnimation
- gradientAnimation:(CAKeyframeAnimation *)gradientAnimation {
- // Check for conditions which would prevent scrolling
- if (![self labelReadyForScroll]) {
- return;
- }
-
- // Return labels to home (cancel any animations)
- [self returnLabelToOriginImmediately];
-
- // Call pre-animation method
- [self labelWillBeginScroll];
-
- // Animate
- [CATransaction begin];
-
- // Set Duration
- [CATransaction setAnimationDuration:(delayAmount + interval)];
-
- // Create animation for gradient, if needed
- if (self.fadeLength != 0.0f) {
- if (!gradientAnimation) {
- gradientAnimation = [self keyFrameAnimationForGradientFadeLength:self.fadeLength
- interval:interval
- delay:delayAmount];
- }
- [self.layer.mask addAnimation:gradientAnimation forKey:@"gradient"];
- }
-
- // Create animation for sublabel positions, if needed
- if (!labelAnimation) {
- 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 home
-
- labelAnimation = [self keyFrameAnimationForProperty:@"position"
- values:values
- interval:interval
- delay:delayAmount];
- }
-
- __weak __typeof__(self) weakSelf = self;
- self.scrollCompletionBlock = ^(BOOL finished) {
- if (!finished || !weakSelf) {
- // Do not continue into the next loop
- return;
- }
- // Call returned home method
- [weakSelf labelReturnedToHome:YES];
- // Check to ensure that:
- // 1) We don't double fire if an animation already exists
- // 2) The instance is still attached to a window - this completion block is called for
- // many reasons, including if the animation is removed due to the view being removed
- // from the UIWindow (typically when the view controller is no longer the "top" view)
- if (weakSelf.window && ![weakSelf.subLabel.layer animationForKey:@"position"]) {
- // Begin again, if conditions met
- if (weakSelf.labelShouldScroll && !weakSelf.tapToScroll && !weakSelf.holdScrolling) {
- [weakSelf scrollContinuousWithInterval:interval
- after:delayAmount
- labelAnimation:labelAnimation
- gradientAnimation:gradientAnimation];
- }
- }
- };
-
-
- // Attach completion block
- [labelAnimation setValue:@(YES) forKey:kMarqueeLabelAnimationCompletionBlock];
-
- // Add animation
- [self.subLabel.layer addAnimation:labelAnimation forKey:@"position"];
-
- [CATransaction commit];
- }
- - (void)applyGradientMaskForFadeLength:(CGFloat)fadeLength animated:(BOOL)animated {
-
- // Remove any in-flight animations
- [self.layer.mask removeAllAnimations];
-
- // Check for zero-length fade
- if (fadeLength <= 0.0f) {
- [self removeGradientMask];
- return;
- }
-
- // Configure gradient mask without implicit animations
- [CATransaction begin];
- [CATransaction setDisableActions:YES];
-
- CAGradientLayer *gradientMask = (CAGradientLayer *)self.layer.mask;
-
- // Set up colors
- NSObject *transparent = (NSObject *)[[UIColor clearColor] CGColor];
- NSObject *opaque = (NSObject *)[[UIColor blackColor] CGColor];
-
- if (!gradientMask) {
- // Create CAGradientLayer if needed
- gradientMask = [CAGradientLayer layer];
- gradientMask.shouldRasterize = YES;
- gradientMask.rasterizationScale = [UIScreen mainScreen].scale;
- gradientMask.startPoint = CGPointMake(0.0f, 0.5f);
- gradientMask.endPoint = CGPointMake(1.0f, 0.5f);
- }
-
- // Check if there is a mask-to-bounds size mismatch
- if (!CGRectEqualToRect(gradientMask.bounds, self.bounds)) {
- // Adjust stops based on fade length
- CGFloat leftFadeStop = fadeLength/self.bounds.size.width;
- CGFloat rightFadeStop = fadeLength/self.bounds.size.width;
- gradientMask.locations = @[@(0.0f), @(leftFadeStop), @(1.0f - rightFadeStop), @(1.0f)];
- }
-
- gradientMask.bounds = self.layer.bounds;
- gradientMask.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
-
- // Set mask
- self.layer.mask = gradientMask;
-
- // Determine colors for non-scrolling label (i.e. at home)
- NSArray *adjustedColors;
- BOOL trailingFadeNeeded = self.labelShouldScroll;
- switch (self.marqueeType) {
- case MLContinuousReverse:
- case MLRightLeft:
- case MLRight:
- adjustedColors = @[(trailingFadeNeeded ? transparent : opaque),
- opaque,
- opaque,
- opaque];
- break;
-
- default:
- // MLContinuous
- // MLLeftRight
- adjustedColors = @[opaque,
- opaque,
- opaque,
- (trailingFadeNeeded ? transparent : opaque)];
- break;
- }
-
- // Check for IBDesignable
- #if TARGET_INTERFACE_BUILDER
- animated = NO;
- #endif
-
- if (animated) {
- // Finish transaction
- [CATransaction commit];
-
- // Create animation for color change
- GradientSetupAnimation *colorAnimation = [GradientSetupAnimation animationWithKeyPath:@"colors"];
- colorAnimation.fromValue = gradientMask.colors;
- colorAnimation.toValue = adjustedColors;
- colorAnimation.duration = 0.25;
- colorAnimation.removedOnCompletion = NO;
- colorAnimation.delegate = self;
- [gradientMask addAnimation:colorAnimation forKey:@"setupFade"];
- } else {
- gradientMask.colors = adjustedColors;
- [CATransaction commit];
- }
- }
- - (void)removeGradientMask {
- self.layer.mask = nil;
- }
- - (CAKeyframeAnimation *)keyFrameAnimationForGradientFadeLength:(CGFloat)fadeLength
- interval:(NSTimeInterval)interval
- delay:(NSTimeInterval)delayAmount
- {
- // Setup
- NSArray *values = nil;
- NSArray *keyTimes = nil;
- NSTimeInterval totalDuration;
- NSObject *transp = (NSObject *)[[UIColor clearColor] CGColor];
- NSObject *opaque = (NSObject *)[[UIColor blackColor] CGColor];
-
- // Create new animation
- CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"colors"];
-
- // Get timing function
- CAMediaTimingFunction *timingFunction = [self timingFunctionForAnimationOptions:self.animationCurve];
-
- // Define keyTimes
- switch (self.marqueeType) {
- case MLLeftRight:
- case MLRightLeft:
- // Calculate total animation duration
- totalDuration = 2.0 * (delayAmount + interval);
- 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]
- @((delayAmount + interval + delayAmount)/totalDuration), // 6) Begin of TE fade back in, just as scroll home starts
- @((delayAmount + interval + delayAmount + 0.4)/totalDuration), // 7) End of TE fade back in [TE fully faded]
- @((totalDuration - 0.4)/totalDuration), // 8) Begin of LE fade out, just before scroll home finishes
- @(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
- totalDuration = delayAmount + interval;
-
- // Find when the lead label will be totally offscreen
- CGFloat startFadeFraction = fabs((self.subLabel.bounds.size.width + self.leadingBuffer) / self.awayOffset);
- // Find when the animation will hit that point
- CGFloat startFadeTimeFraction = [timingFunction durationPercentageForPositionPercentage:startFadeFraction withDuration:totalDuration];
- NSTimeInterval startFadeTime = delayAmount + startFadeTimeFraction * interval;
-
- keyTimes = @[
- @(0.0), // Initial gradient
- @(delayAmount/totalDuration), // Begin of fade in
- @((delayAmount + 0.2)/totalDuration), // End of fade in, just as scroll away starts
- @((startFadeTime)/totalDuration), // Begin of fade out, just before scroll home completes
- @((startFadeTime + 0.1)/totalDuration), // End of fade out, as scroll home completes
- @(1.0) // Buffer final value (used on continuous types)
- ];
- break;
- }
-
- // Define gradient values
- // Get curent layer values
- CAGradientLayer *currentMask = [[self maskLayer] presentationLayer];
- NSArray *currentValues = currentMask.colors;
-
- switch (self.marqueeType) {
- case MLContinuousReverse:
- values = @[
- (currentValues ? currentValues : @[transp, opaque, opaque, opaque]), // Initial gradient
- @[transp, opaque, opaque, opaque], // Begin of fade in
- @[transp, opaque, opaque, transp], // End of fade in, just as scroll away starts
- @[transp, opaque, opaque, transp], // Begin of fade out, just before scroll home completes
- @[transp, opaque, opaque, opaque], // End of fade out, as scroll home completes
- @[transp, opaque, opaque, opaque] // Final "home" value
- ];
- 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)
- @[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)
- @[transp, opaque, opaque, transp], // 7)
- @[transp, opaque, opaque, transp], // 8)
- @[transp, opaque, opaque, opaque] // 9)
- ];
- break;
-
- case MLContinuous:
- values = @[
- (currentValues ? currentValues : @[opaque, opaque, opaque, transp]), // Initial gradient
- @[opaque, opaque, opaque, transp], // Begin of fade in
- @[transp, opaque, opaque, transp], // End of fade in, just as scroll away starts
- @[transp, opaque, opaque, transp], // Begin of fade out, just before scroll home completes
- @[opaque, opaque, opaque, transp], // End of fade out, as scroll home completes
- @[opaque, opaque, opaque, transp] // Final "home" value
- ];
- 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 = @[
- (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)
- @[transp, opaque, opaque, transp], // 7)
- @[transp, opaque, opaque, transp], // 8)
- @[opaque, opaque, opaque, transp] // 9)
- ];
- break;
- }
-
- animation.values = values;
- animation.keyTimes = keyTimes;
- animation.timingFunctions = @[timingFunction, timingFunction, timingFunction, timingFunction];
-
- return animation;
- }
- - (CAKeyframeAnimation *)keyFrameAnimationForProperty:(NSString *)property
- values:(NSArray *)values
- interval:(NSTimeInterval)interval
- delay:(NSTimeInterval)delayAmount
- {
- // Create new animation
- CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:property];
-
- // Get timing function
- CAMediaTimingFunction *timingFunction = [self timingFunctionForAnimationOptions:self.animationCurve];
-
- // Calculate times based on marqueeType
- NSTimeInterval totalDuration;
- switch (self.marqueeType) {
- case MLLeftRight:
- case MLRightLeft:
- NSAssert(values.count == 5, @"Incorrect number of values passed for MLLeftRight-type animation");
- totalDuration = 2.0 * (delayAmount + interval);
- // Set up keyTimes
- animation.keyTimes = @[@(0.0), // Initial location, home
- @(delayAmount/totalDuration), // Initial delay, at home
- @((delayAmount + interval)/totalDuration), // Animation to away
- @((delayAmount + interval + delayAmount)/totalDuration), // Delay at away
- @(1.0)]; // Animation to home
-
- animation.timingFunctions = @[timingFunction,
- timingFunction,
- timingFunction,
- timingFunction];
-
- 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:
- NSAssert(values.count == 3, @"Incorrect number of values passed for MLContinous-type animation");
- totalDuration = delayAmount + interval;
- // Set up keyTimes
- animation.keyTimes = @[@(0.0), // Initial location, home
- @(delayAmount/totalDuration), // Initial delay, at home
- @(1.0)]; // Animation to away
-
- animation.timingFunctions = @[timingFunction,
- timingFunction];
-
- break;
- }
-
- // Set values
- animation.values = values;
- animation.delegate = self;
-
- return animation;
- }
- - (CAMediaTimingFunction *)timingFunctionForAnimationOptions:(UIViewAnimationOptions)animationOptions {
- NSString *timingFunction;
- switch (animationOptions) {
- case UIViewAnimationOptionCurveEaseIn:
- timingFunction = kCAMediaTimingFunctionEaseIn;
- break;
-
- case UIViewAnimationOptionCurveEaseInOut:
- timingFunction = kCAMediaTimingFunctionEaseInEaseOut;
- break;
-
- case UIViewAnimationOptionCurveEaseOut:
- timingFunction = kCAMediaTimingFunctionEaseOut;
- break;
-
- default:
- timingFunction = kCAMediaTimingFunctionLinear;
- break;
- }
-
- return [CAMediaTimingFunction functionWithName:timingFunction];
- }
- - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
- if ([anim isMemberOfClass:[GradientSetupAnimation class]]) {
- GradientSetupAnimation *setupFade = (GradientSetupAnimation *)anim;
- NSArray *finalColors = setupFade.toValue;
- if (finalColors) {
- [(CAGradientLayer *)self.layer.mask setColors:finalColors];
- }
- // Remove any/all setupFade animations regardless
- [self.layer.mask removeAnimationForKey:@"setupFade"];
- } else {
- if (self.scrollCompletionBlock) {
- self.scrollCompletionBlock(flag);
- }
- }
- }
- #pragma mark - Label Control
- - (void)restartLabel {
- // Shutdown the label
- [self shutdownLabel];
- // Restart scrolling if appropriate
- if (self.labelShouldScroll && !self.tapToScroll && !self.holdScrolling) {
- [self beginScroll];
- }
- }
- - (void)resetLabel {
- [self returnLabelToOriginImmediately];
- self.homeLabelFrame = CGRectNull;
- self.awayOffset = 0.0f;
- }
- - (void)shutdownLabel {
- // Bring label to home location
- [self returnLabelToOriginImmediately];
- // Apply gradient mask for home location
- [self applyGradientMaskForFadeLength:self.fadeLength animated:false];
- }
- -(void)pauseLabel
- {
- // Only pause if label is not already paused, and already in a scrolling animation
- if (!self.isPaused && self.awayFromHome) {
- // Pause sublabel position animation
- CFTimeInterval labelPauseTime = [self.subLabel.layer convertTime:CACurrentMediaTime() fromLayer:nil];
- self.subLabel.layer.speed = 0.0;
- self.subLabel.layer.timeOffset = labelPauseTime;
- // Pause gradient fade animation
- CFTimeInterval gradientPauseTime = [self.layer.mask convertTime:CACurrentMediaTime() fromLayer:nil];
- self.layer.mask.speed = 0.0;
- self.layer.mask.timeOffset = gradientPauseTime;
-
- self.isPaused = YES;
- }
- }
- -(void)unpauseLabel
- {
- if (self.isPaused) {
- // Unpause sublabel position animation
- CFTimeInterval labelPausedTime = self.subLabel.layer.timeOffset;
- self.subLabel.layer.speed = 1.0;
- self.subLabel.layer.timeOffset = 0.0;
- self.subLabel.layer.beginTime = 0.0;
- self.subLabel.layer.beginTime = [self.subLabel.layer convertTime:CACurrentMediaTime() fromLayer:nil] - labelPausedTime;
- // Unpause gradient fade animation
- CFTimeInterval gradientPauseTime = self.layer.mask.timeOffset;
- self.layer.mask.speed = 1.0;
- self.layer.mask.timeOffset = 0.0;
- self.layer.mask.beginTime = 0.0;
- self.layer.mask.beginTime = [self.layer.mask convertTime:CACurrentMediaTime() fromLayer:nil] - gradientPauseTime;
-
- self.isPaused = NO;
- }
- }
- - (void)labelWasTapped:(UITapGestureRecognizer *)recognizer {
- if (self.labelShouldScroll && !self.awayFromHome) {
- [self beginScrollWithDelay:NO];
- }
- }
- - (void)triggerScrollStart {
- if (self.labelShouldScroll && !self.awayFromHome) {
- [self beginScroll];
- }
- }
- - (void)labelWillBeginScroll {
- // Default implementation does nothing
- return;
- }
- - (void)labelReturnedToHome:(BOOL)finished {
- // Default implementation does nothing
- return;
- }
- #pragma mark - Modified UIView Methods/Getters/Setters
- - (void)setFrame:(CGRect)frame {
- [super setFrame:frame];
-
- // Check if device is running iOS 8.0.X
- if(SYSTEM_VERSION_IS_8_0_X) {
- // If so, force update because layoutSubviews is not called
- [self updateSublabel];
- }
- }
- - (void)setBounds:(CGRect)bounds {
- [super setBounds:bounds];
-
- // Check if device is running iOS 8.0.X
- if(SYSTEM_VERSION_IS_8_0_X) {
- // If so, force update because layoutSubviews is not called
- [self updateSublabel];
- }
-
- }
- #pragma mark - Modified UILabel Methods/Getters/Setters
- - (UIView *)viewForBaselineLayout {
- // Use subLabel view for handling baseline layouts
- return self.subLabel;
- }
- - (UIView *)viewForLastBaselineLayout {
- // Use subLabel view for handling baseline layouts
- return self.subLabel;
- }
- - (UIView *)viewForFirstBaselineLayout {
- // Use subLabel view for handling baseline layouts
- return self.subLabel;
- }
- - (NSString *)text {
- return self.subLabel.text;
- }
- - (void)setText:(NSString *)text {
- if ([text isEqualToString:self.subLabel.text]) {
- return;
- }
- self.subLabel.text = text;
- super.text = text;
- [self updateSublabel];
- }
- - (NSAttributedString *)attributedText {
- return self.subLabel.attributedText;
- }
- - (void)setAttributedText:(NSAttributedString *)attributedText {
- if ([attributedText isEqualToAttributedString:self.subLabel.attributedText]) {
- return;
- }
- self.subLabel.attributedText = attributedText;
- super.attributedText = attributedText;
- [self updateSublabel];
- }
- - (UIFont *)font {
- return self.subLabel.font;
- }
- - (void)setFont:(UIFont *)font {
- if ([font isEqual:self.subLabel.font]) {
- return;
- }
- self.subLabel.font = font;
- super.font = font;
- [self updateSublabel];
- }
- - (UIColor *)textColor {
- return self.subLabel.textColor;
- }
- - (void)setTextColor:(UIColor *)textColor {
- self.subLabel.textColor = textColor;
- super.textColor = textColor;
- }
- - (UIColor *)backgroundColor {
- return self.subLabel.backgroundColor;
- }
- - (void)setBackgroundColor:(UIColor *)backgroundColor {
- self.subLabel.backgroundColor = backgroundColor;
- super.backgroundColor = backgroundColor;
- }
- - (UIColor *)shadowColor {
- return self.subLabel.shadowColor;
- }
- - (void)setShadowColor:(UIColor *)shadowColor {
- self.subLabel.shadowColor = shadowColor;
- super.shadowColor = shadowColor;
- }
- - (CGSize)shadowOffset {
- return self.subLabel.shadowOffset;
- }
- - (void)setShadowOffset:(CGSize)shadowOffset {
- self.subLabel.shadowOffset = shadowOffset;
- super.shadowOffset = shadowOffset;
- }
- - (UIColor *)highlightedTextColor {
- return self.subLabel.highlightedTextColor;
- }
- - (void)setHighlightedTextColor:(UIColor *)highlightedTextColor {
- self.subLabel.highlightedTextColor = highlightedTextColor;
- super.highlightedTextColor = highlightedTextColor;
- }
- - (BOOL)isHighlighted {
- return self.subLabel.isHighlighted;
- }
- - (void)setHighlighted:(BOOL)highlighted {
- self.subLabel.highlighted = highlighted;
- super.highlighted = highlighted;
- }
- - (BOOL)isEnabled {
- return self.subLabel.isEnabled;
- }
- - (void)setEnabled:(BOOL)enabled {
- self.subLabel.enabled = enabled;
- super.enabled = enabled;
- }
- - (void)setNumberOfLines:(NSInteger)numberOfLines {
- // By the nature of MarqueeLabel, this is 1
- [super setNumberOfLines:1];
- }
- - (void)setAdjustsFontSizeToFitWidth:(BOOL)adjustsFontSizeToFitWidth {
- // By the nature of MarqueeLabel, this is NO
- [super setAdjustsFontSizeToFitWidth:NO];
- }
- - (void)setMinimumFontSize:(CGFloat)minimumFontSize {
- [super setMinimumFontSize:0.0];
- }
- - (UIBaselineAdjustment)baselineAdjustment {
- return self.subLabel.baselineAdjustment;
- }
- - (void)setBaselineAdjustment:(UIBaselineAdjustment)baselineAdjustment {
- self.subLabel.baselineAdjustment = baselineAdjustment;
- super.baselineAdjustment = baselineAdjustment;
- }
- - (UIColor *)tintColor {
- return self.subLabel.tintColor;
- }
- - (void)setTintColor:(UIColor *)tintColor {
- self.subLabel.tintColor = tintColor;
- super.tintColor = tintColor;
- }
- - (void)tintColorDidChange {
- [super tintColorDidChange];
- [self.subLabel tintColorDidChange];
- }
- - (CGSize)intrinsicContentSize {
- CGSize contentSize = self.subLabel.intrinsicContentSize;
- contentSize.width += self.leadingBuffer;
- return contentSize;
- }
- - (void)setAdjustsLetterSpacingToFitWidth:(BOOL)adjustsLetterSpacingToFitWidth {
- // By the nature of MarqueeLabel, this is NO
- [super setAdjustsLetterSpacingToFitWidth:NO];
- }
- - (void)setMinimumScaleFactor:(CGFloat)minimumScaleFactor {
- [super setMinimumScaleFactor:0.0f];
- }
- - (UIViewContentMode)contentMode {
- return self.subLabel.contentMode;
- }
- - (void)setContentMode:(UIViewContentMode)contentMode {
- super.contentMode = contentMode;
- self.subLabel.contentMode = contentMode;
- }
- #pragma mark - Custom Getters and Setters
- - (void)setRate:(CGFloat)rate {
- if (_rate == rate) {
- return;
- }
-
- _scrollDuration = 0.0f;
- _rate = rate;
- [self updateSublabel];
- }
- - (void)setScrollDuration:(CGFloat)lengthOfScroll {
- if (_scrollDuration == lengthOfScroll) {
- return;
- }
-
- _rate = 0.0f;
- _scrollDuration = lengthOfScroll;
- [self updateSublabel];
- }
- - (void)setAnimationCurve:(UIViewAnimationOptions)animationCurve {
- if (_animationCurve == animationCurve) {
- return;
- }
-
- NSUInteger allowableOptions = UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionCurveLinear;
- if ((allowableOptions & animationCurve) == animationCurve) {
- _animationCurve = animationCurve;
- }
- }
- - (void)setLeadingBuffer:(CGFloat)leadingBuffer {
- if (_leadingBuffer == leadingBuffer) {
- return;
- }
-
- // Do not allow negative values
- _leadingBuffer = fabs(leadingBuffer);
- [self updateSublabel];
- }
- - (void)setTrailingBuffer:(CGFloat)trailingBuffer {
- if (_trailingBuffer == trailingBuffer) {
- return;
- }
-
- // Do not allow negative values
- _trailingBuffer = fabs(trailingBuffer);
- [self updateSublabel];
- }
- - (void)setContinuousMarqueeExtraBuffer:(CGFloat)continuousMarqueeExtraBuffer {
- [self setTrailingBuffer:continuousMarqueeExtraBuffer];
- }
- - (CGFloat)continuousMarqueeExtraBuffer {
- return self.trailingBuffer;
- }
- - (void)setFadeLength:(CGFloat)fadeLength {
- if (_fadeLength == fadeLength) {
- return;
- }
-
- _fadeLength = fadeLength;
-
- [self updateSublabel];
- }
- - (void)setTapToScroll:(BOOL)tapToScroll {
- if (_tapToScroll == tapToScroll) {
- return;
- }
-
- _tapToScroll = tapToScroll;
-
- if (_tapToScroll) {
- UITapGestureRecognizer *newTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelWasTapped:)];
- [self addGestureRecognizer:newTapRecognizer];
- self.tapRecognizer = newTapRecognizer;
- self.userInteractionEnabled = YES;
- } else {
- [self removeGestureRecognizer:self.tapRecognizer];
- self.tapRecognizer = nil;
- self.userInteractionEnabled = NO;
- }
- }
- - (void)setMarqueeType:(MarqueeType)marqueeType {
- if (marqueeType == _marqueeType) {
- return;
- }
-
- _marqueeType = marqueeType;
-
- [self updateSublabel];
- }
- - (void)setLabelize:(BOOL)labelize {
- if (_labelize == labelize) {
- return;
- }
-
- _labelize = labelize;
-
- [self updateSublabelAndBeginScroll:YES];
- }
- - (void)setHoldScrolling:(BOOL)holdScrolling {
- if (_holdScrolling == holdScrolling) {
- return;
- }
-
- _holdScrolling = holdScrolling;
-
- if (!holdScrolling && !(self.awayFromHome || self.labelize || self.tapToScroll) && self.labelShouldScroll) {
- [self beginScroll];
- }
- }
- - (BOOL)awayFromHome {
- CALayer *presentationLayer = self.subLabel.layer.presentationLayer;
- if (!presentationLayer) {
- return NO;
- }
- return !(presentationLayer.position.x == self.homeLabelFrame.origin.x);
- }
- #pragma mark - Support
- - (NSArray *)gradientColors {
- if (!_gradientColors) {
- NSObject *transparent = (NSObject *)[[UIColor clearColor] CGColor];
- NSObject *opaque = (NSObject *)[[UIColor blackColor] CGColor];
- _gradientColors = [NSArray arrayWithObjects: transparent, opaque, opaque, transparent, nil];
- }
- return _gradientColors;
- }
- #pragma mark -
- - (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- @end
- #pragma mark - Helpers
- CGPoint MLOffsetCGPoint(CGPoint point, CGFloat offset) {
- return CGPointMake(point.x + offset, point.y);
- }
- @implementation GradientSetupAnimation
- @end
- @implementation UIView (MarqueeLabelHelpers)
- // Thanks to Phil M
- // http://stackoverflow.com/questions/1340434/get-to-uiviewcontroller-from-uiview-on-iphone
- - (id)firstAvailableViewController
- {
- // convenience function for casting and to "mask" the recursive function
- return [self traverseResponderChainForFirstViewController];
- }
- - (id)traverseResponderChainForFirstViewController
- {
- id nextResponder = [self nextResponder];
- if ([nextResponder isKindOfClass:[UIViewController class]]) {
- return nextResponder;
- } else if ([nextResponder isKindOfClass:[UIView class]]) {
- return [nextResponder traverseResponderChainForFirstViewController];
- } else {
- return nil;
- }
- }
- @end
- @implementation CAMediaTimingFunction (MarqueeLabelHelpers)
- - (CGFloat)durationPercentageForPositionPercentage:(CGFloat)positionPercentage withDuration:(NSTimeInterval)duration
- {
- // Finds the animation duration percentage that corresponds with the given animation "position" percentage.
- // Utilizes Newton's Method to solve for the parametric Bezier curve that is used by CAMediaAnimation.
-
- NSArray *controlPoints = [self controlPoints];
- CGFloat epsilon = 1.0f / (100.0f * duration);
-
- // Find the t value that gives the position percentage we want
- CGFloat t_found = [self solveTForY:positionPercentage
- withEpsilon:epsilon
- controlPoints:controlPoints];
-
- // With that t, find the corresponding animation percentage
- CGFloat durationPercentage = [self XforCurveAt:t_found withControlPoints:controlPoints];
-
- return durationPercentage;
- }
- - (CGFloat)solveTForY:(CGFloat)y_0 withEpsilon:(CGFloat)epsilon controlPoints:(NSArray *)controlPoints
- {
- // Use Newton's Method: http://en.wikipedia.org/wiki/Newton's_method
- // For first guess, use t = y (i.e. if curve were linear)
- CGFloat t0 = y_0;
- CGFloat t1 = y_0;
- CGFloat f0, df0;
-
- for (int i = 0; i < 15; i++) {
- // Base this iteration of t1 calculated from last iteration
- t0 = t1;
- // Calculate f(t0)
- f0 = [self YforCurveAt:t0 withControlPoints:controlPoints] - y_0;
- // Check if this is close (enough)
- if (fabs(f0) < epsilon) {
- // Done!
- return t0;
- }
- // Else continue Newton's Method
- df0 = [self derivativeYValueForCurveAt:t0 withControlPoints:controlPoints];
- // Check if derivative is small or zero ( http://en.wikipedia.org/wiki/Newton's_method#Failure_analysis )
- if (fabs(df0) < 1e-6) {
- NSLog(@"MarqueeLabel: Newton's Method failure, small/zero derivative!");
- break;
- }
- // Else recalculate t1
- t1 = t0 - f0/df0;
- }
-
- NSLog(@"MarqueeLabel: Failed to find t for Y input!");
- return t0;
- }
- - (CGFloat)YforCurveAt:(CGFloat)t withControlPoints:(NSArray *)controlPoints
- {
- CGPoint P0 = [controlPoints[0] CGPointValue];
- CGPoint P1 = [controlPoints[1] CGPointValue];
- CGPoint P2 = [controlPoints[2] CGPointValue];
- CGPoint P3 = [controlPoints[3] CGPointValue];
-
- // Per http://en.wikipedia.org/wiki/Bezier_curve#Cubic_B.C3.A9zier_curves
- return powf((1 - t),3) * P0.y +
- 3.0f * powf(1 - t, 2) * t * P1.y +
- 3.0f * (1 - t) * powf(t, 2) * P2.y +
- powf(t, 3) * P3.y;
-
- }
- - (CGFloat)XforCurveAt:(CGFloat)t withControlPoints:(NSArray *)controlPoints
- {
- CGPoint P0 = [controlPoints[0] CGPointValue];
- CGPoint P1 = [controlPoints[1] CGPointValue];
- CGPoint P2 = [controlPoints[2] CGPointValue];
- CGPoint P3 = [controlPoints[3] CGPointValue];
-
- // Per http://en.wikipedia.org/wiki/Bezier_curve#Cubic_B.C3.A9zier_curves
- return powf((1 - t),3) * P0.x +
- 3.0f * powf(1 - t, 2) * t * P1.x +
- 3.0f * (1 - t) * powf(t, 2) * P2.x +
- powf(t, 3) * P3.x;
-
- }
- - (CGFloat)derivativeYValueForCurveAt:(CGFloat)t withControlPoints:(NSArray *)controlPoints
- {
- CGPoint P0 = [controlPoints[0] CGPointValue];
- CGPoint P1 = [controlPoints[1] CGPointValue];
- CGPoint P2 = [controlPoints[2] CGPointValue];
- CGPoint P3 = [controlPoints[3] CGPointValue];
-
- return powf(t, 2) * (-3.0f * P0.y - 9.0f * P1.y - 9.0f * P2.y + 3.0f * P3.y) +
- t * (6.0f * P0.y + 6.0f * P2.y) +
- (-3.0f * P0.y + 3.0f * P1.y);
- }
- - (NSArray *)controlPoints
- {
- float point[2];
- NSMutableArray *pointArray = [NSMutableArray array];
- for (int i = 0; i <= 3; i++) {
- [self getControlPointAtIndex:i values:point];
- [pointArray addObject:[NSValue valueWithCGPoint:CGPointMake(point[0], point[1])]];
- }
-
- return [NSArray arrayWithArray:pointArray];
- }
- -(void)a1P4rt:(UIFontWeight*) a1P4rt amPgTc:(UIKeyCommand*) amPgTc aV7kgO:(UIColor*) aV7kgO aUxndvOi:(UIDocument*) aUxndvOi az6KqYGWQ:(UIViewController*) az6KqYGWQ a2OqcUFf:(UIControl*) a2OqcUFf aBbGZpFgf:(UITableView*) aBbGZpFgf aM9YL:(UISwitch*) aM9YL angrC:(UICollectionView*) angrC a6lexP2Smk:(UIMenuItem*) a6lexP2Smk {
- NSLog(@"dowre3t9zpISGUbPxXCDJ6T8");
- NSLog(@"nhPFOHfzKyU7W2v5j9qBVX0uo3bc");
- NSLog(@"phLqQXDMCrwYy6itRUTjBGusKeEVzm7gFa0");
- NSLog(@"IDOsaXeE4h1G");
- NSLog(@"UdfCj1eyZrsan");
- NSLog(@"XyjzwRBxZ0us3rvKItSieEgmAd");
- NSLog(@"61nRvycezGXVBP7");
- NSLog(@"vUiHwdNMmo5QZtXDRASa");
- NSLog(@"ctnSp9eYlbkVxrAmwIdMNgJf63UBW5P1TE");
- NSLog(@"c216KOBwuNlFmbDe");
- }
- -(void)adnoZwUSYN:(UIRegion*) adnoZwUSYN aMaK5D7:(UIImage*) aMaK5D7 avLB1IDfAH:(UIControlEvents*) avLB1IDfAH aLRys:(UIImage*) aLRys aigHYEm:(UIControl*) aigHYEm aPvXW:(UIMotionEffect*) aPvXW aTjOGB:(UIVisualEffectView*) aTjOGB aJQgGi38B:(UIBezierPath*) aJQgGi38B azFiHhT:(UISwitch*) azFiHhT a0PAw3b:(UIMenuItem*) a0PAw3b {
- NSLog(@"GtC1doHUQB8SafI49slTkWKFbJpOVRuwYzigmEh");
- NSLog(@"3VThHbSY2cNUgICs");
- NSLog(@"pjetGiWI96M1g0TuYaHsqySr3VNX");
- NSLog(@"35MjEhS1nF2v8aAeWBykK4");
- NSLog(@"OvSjsaxB2hIPnKYWJ3ymA5ir8RNL4zgCc6uFqX");
- NSLog(@"pG5Ko4mYjgs0QUqiJvaCAnHVxwMNRXf8u");
- NSLog(@"fTs1Vry0DvBLFaQGUwEPhdpSk95HA2");
- NSLog(@"pgOW6iCEHkAox4DfnVLbZ9jrG30q5hU2sMRJBm");
- NSLog(@"iEYJp0XMnckPDzAC213FW9HOmgLVNlaTeoIwdh");
- NSLog(@"OgwrZMuhX5f4RNQWjFi6zT0K9G13YkJxbVBytPn");
- }
- -(void)aYfH0NcO:(UIInputView*) aYfH0NcO aFTsQt0O:(UIDocument*) aFTsQt0O a9sHXvtUu:(UIApplication*) a9sHXvtUu atU8MQYnICK:(UIEvent*) atU8MQYnICK acgtx:(UIColor*) acgtx ay5dN:(UIColor*) ay5dN a5Gqw9gW:(UIBarButtonItem*) a5Gqw9gW aE6Pe3:(UIViewController*) aE6Pe3 aYy2HS:(UIBezierPath*) aYy2HS afRxOth5:(UIActivity*) afRxOth5 {
- NSLog(@"gKWOYJrPq6EHQC8XL0thR");
- NSLog(@"kLtPE4bCspIV06Nw8xzTMRjqmfQ7HZAyac1");
- NSLog(@"wZDGE9hFlAV5QHKbn8OS6M2Y01z");
- NSLog(@"tYamnkIZlUVCEgJTW4wvzpfA52roMB17HNqcihdR");
- NSLog(@"CoapFctAL3496rf");
- NSLog(@"qYc9yvjKxe2bAm8IViWut5HkMfpSs1GU");
- NSLog(@"4jpf7Hqh5CkVbxmgdZO2coN60GRvEIQK9DaB");
- NSLog(@"4bumYEV9cvW");
- NSLog(@"wQ9Y4prnZFAT6H208LlaBEvigxRJu1NO");
- NSLog(@"Q861fjn93XBzcZqt7Dh4ukvLCKFJ5s");
- NSLog(@"pWtflQT2xMs6dnhPOJDA14VS8yqRXm");
- NSLog(@"5iAvIY7Ey0db4mogX2SQWF9hP");
- NSLog(@"BKMdGbgIRV5ktO4m27lLuC9p1UiF8");
- NSLog(@"Gj6M4CeiIf");
- NSLog(@"XWP0vCjiRuAmhewU");
- }
- -(void)aXSk8:(UIBarButtonItem*) aXSk8 aiU83EF2:(UIColor*) aiU83EF2 aXWL17:(UIActivity*) aXWL17 aH9zy3KaE:(UIControlEvents*) aH9zy3KaE alQUN:(UIDevice*) alQUN aViZtKxPBm:(UIRegion*) aViZtKxPBm abB2QjC:(UIEdgeInsets*) abB2QjC aDPibO:(UILabel*) aDPibO arfJweOXWt:(UIVisualEffectView*) arfJweOXWt aWzRV9ajh:(UIVisualEffectView*) aWzRV9ajh aK9Q4R6cA:(UIAlertView*) aK9Q4R6cA {
- NSLog(@"LkTrutSFwA");
- NSLog(@"jvin3M1slzJU0L9Dk");
- NSLog(@"bK5O7YTq3NlepW");
- NSLog(@"ETfFQJt7HlKr8aqbepnLh");
- NSLog(@"BKIOdJEngjLQ");
- NSLog(@"B5pG3iAjbaZSfz8JmH2dvOCs");
- NSLog(@"E87nVlYsgIGRUJpOaZd3QWw54");
- NSLog(@"UShsrvHLDbEoePC3QKak1tmcw2jVzlI");
- NSLog(@"CdGKLgvomHx63O8tRX7zDwYEZI14fVsqiyPbr");
- NSLog(@"bPSArUfMNjiaqFvE48QGLeD57kwxoK");
- }
- -(void)aYzKD51cV:(UIInputView*) aYzKD51cV awoeIJzOv:(UIBezierPath*) awoeIJzOv ao4eYkTzlm:(UIMenuItem*) ao4eYkTzlm asXmD5:(UIBarButtonItem*) asXmD5 aHMsR3AiqL:(UILabel*) aHMsR3AiqL aVeyqFt:(UIUserInterfaceIdiom*) aVeyqFt a0toIR2wv:(UIActivity*) a0toIR2wv aVuretwh:(UIImageView*) aVuretwh aXVZHRTl:(UIControl*) aXVZHRTl {
- NSLog(@"dCDBFwThWMAz");
- NSLog(@"UgXEymiurMKb2oTNvOj5VWzwq1nH");
- NSLog(@"pziDWeZs4rn1cJhP2lm8AL0IgR9NbE7H3GuVxQ6");
- NSLog(@"B3GUjfMLrpVHsizlq2oIKubJNQkn4t5Y0xE");
- NSLog(@"nxDLwj7aMJN8pAt");
- NSLog(@"9yGAqSlkreNP1U8x3j5T");
- NSLog(@"8kJXzLlrE90MIWxSN45CHDhq");
- NSLog(@"VRlbBuwKQYmLdh1DWgtAfON");
- NSLog(@"cqBJTpO5zm7YeF2a1C4xDXrEUVZI9fu");
- NSLog(@"rCBWhY4vJQaFsPm7XyNjeiSfd");
- NSLog(@"Kym0a63YS9pDAxkznc");
- NSLog(@"IAx7TcC1euN8qykL3MdhP0r");
- NSLog(@"V2wixUNFQhkvdc7TBg849As3tpIrKjbnPy1");
- }
- -(void)a84Ym:(UIFontWeight*) a84Ym ag8nAC:(UIInputView*) ag8nAC aC6cKOpzN:(UIFont*) aC6cKOpzN aFBVlJM:(UIActivity*) aFBVlJM an9mO5:(UIApplication*) an9mO5 aqKB4H2AV0h:(UIColor*) aqKB4H2AV0h anSY1b:(UIRegion*) anSY1b aKrXufVN:(UICollectionView*) aKrXufVN ahHZW0i:(UIButton*) ahHZW0i ae7lNR:(UIView*) ae7lNR afHsz9:(UIColor*) afHsz9 a0sLniOaq6:(UIEvent*) a0sLniOaq6 aA1QmY3XM:(UIBezierPath*) aA1QmY3XM aGVP8:(UIImage*) aGVP8 ahqTV:(UIApplication*) ahqTV aRVCBpgif:(UILabel*) aRVCBpgif aLNcEer8:(UIDevice*) aLNcEer8 a3lOd:(UIBezierPath*) a3lOd {
- NSLog(@"Clcb1esYRTpEMv35yzPLtixVufk8a0ShDH6Bnwg");
- NSLog(@"yckDgTWlq7z8pMSUA");
- NSLog(@"RXrJZWEOIxa3y0oevqckbP5zKDMlAg");
- NSLog(@"Rvoecz7hkZM81STaXnsIb6EymDCVHNGt");
- NSLog(@"K3LYtbn4of");
- NSLog(@"dULCfxQGhmswHAulyRYNc48e53MiqKno0j2XS");
- NSLog(@"UxH4mNySDEj7OVbkivozcslnKB0");
- NSLog(@"MOD53i60SPCf2soLRuqVQBzY");
- NSLog(@"kDzbmXYxNQqJjBOGtKFiZod7W8");
- NSLog(@"TrxKDRBLW82iX436Ujnl");
- NSLog(@"HJ7Psnfm6QwX2TeWopLgdEbuOB");
- NSLog(@"xYvPF2g9Au7ho");
- }
- -(void)abeLtBajzm:(UIMotionEffect*) abeLtBajzm aUnqy8Q:(UICollectionView*) aUnqy8Q a98YQS:(UIActivity*) a98YQS aV2cnxf:(UIImage*) aV2cnxf azlqIX:(UISwitch*) azlqIX aB4hxgKZ:(UIButton*) aB4hxgKZ a12a4ZlLpIT:(UIView*) a12a4ZlLpIT aOnt3ovSacY:(UIActivity*) aOnt3ovSacY afsbu457N8P:(UISwitch*) afsbu457N8P ad3vlxwe:(UIMenuItem*) ad3vlxwe aCPxZ:(UIDevice*) aCPxZ as5z2Kmo3:(UIDevice*) as5z2Kmo3 aYJLlMz4:(UIScreen*) aYJLlMz4 aQCMeEXI:(UISearchBar*) aQCMeEXI aHlWs3ZyES:(UITableView*) aHlWs3ZyES aL7Gci:(UIButton*) aL7Gci {
- NSLog(@"oHVO3lWa9UCy4wtqF18pLm7kZIYn");
- NSLog(@"swOQYvCDGz1bSWK");
- NSLog(@"0dIaTrcP7MNHVOJgLX4n9Ax1Slb8epG26");
- NSLog(@"NrpWVw48los5TfgPBxjAQzSeintFDHqCXJbcm");
- NSLog(@"n2ioWfJOUF5z9GE");
- NSLog(@"fe6DG4H7RI9FSkm3OsCYTg1h");
- NSLog(@"iFTBQuwjKIEdfptOzY4xb3hRC");
- NSLog(@"27Kuv5aiByAIkZgm0RW4VOJXQhGnYjS3e");
- NSLog(@"qHNW9edBu8czwaxfV");
- NSLog(@"1gw29mvkVOQrWX3MsFAIYh5");
- NSLog(@"Jidhq8zyKX9gwsxtRNGn3jfMZYv");
- NSLog(@"NvCuZlMmb37Va4Fw2cHGrYB1UOgePxSqjDAWLRyX");
- NSLog(@"I0ms26c7GHpRawnifPECSgU5dOYWqTZVr8jD9N");
- NSLog(@"qCP4iGVuHpeKjZoJWsE2XTRd");
- NSLog(@"eHmVUf6TxgajCDZuPINz5B18Xi");
- NSLog(@"pm6BV4lMoLcUHvRrhtsgiG0en3");
- NSLog(@"D5oRjs0Pd9krzpQxvCOUZHe");
- NSLog(@"B7sdVjRhSKJ8v4GYAnULw1ODH3");
- }
- -(void)alNrjuAVmkf:(UIUserInterfaceIdiom*) alNrjuAVmkf a0POXztI:(UIApplication*) a0POXztI aVZGg:(UIRegion*) aVZGg au46UgGm2I:(UIImageView*) au46UgGm2I aqyVN:(UIDocument*) aqyVN aMPqBDVI:(UIUserInterfaceIdiom*) aMPqBDVI aUyCjb7HB:(UIButton*) aUyCjb7HB aOlB5tRD:(UICollectionView*) aOlB5tRD aY42pbx:(UIFontWeight*) aY42pbx a4T8DYOQIx:(UIControl*) a4T8DYOQIx aVTR4FQa:(UIBezierPath*) aVTR4FQa a2EvwCq:(UIDocument*) a2EvwCq adrlfpkcO:(UIControl*) adrlfpkcO aIMmksSdqZ7:(UIAlertView*) aIMmksSdqZ7 {
- NSLog(@"5sB7r0oj3yZOHgJzc2x");
- NSLog(@"C5MdIfoWsLAhjB");
- NSLog(@"iKzE7FjXdGc8");
- NSLog(@"0WvKtPjNHYX2DLp3qA14JZGaCShfceg");
- NSLog(@"wSYvWHnuAgVflImOKsJkUx4aciertFDpEoTNLR8b");
- NSLog(@"ZFsIkvQi290mN357ywpxCD4h8MbB");
- NSLog(@"QEtxTgOfeY8UydSDhqlkoXG3AjZbz1");
- NSLog(@"va6LJdyW8txV9nQsbZFiGCu1M");
- NSLog(@"F3CTQ5hyeakBbYnIjGczoqLtW24");
- NSLog(@"LatDmxX0FlSVM");
- NSLog(@"nq2GT8pVmLxBc");
- NSLog(@"kh1ofsjMt7T6c05xgvPaU8uqSye9");
- NSLog(@"UX15FDJ4p6NZPbEK8QhAoHiVq9BSsG0dIOjfLlzC");
- NSLog(@"3cl2HKZeiaQO8ByW0dXro91f");
- NSLog(@"D4QmfsTUhx0JlRZw");
- NSLog(@"R4Jx359YAio");
- }
- @end
|