iPhone “slide to unlock” animation

前端 未结 13 701
感情败类
感情败类 2020-11-28 00:49

Any ideas as to how Apple implemented the \"slide to unlock\" (also, \"slide to power off\" is another identical example) animation?

I thought about some sort of ani

相关标签:
13条回答
  • 2020-11-28 01:26

    Maybe it's just a rendered-out animation - you know, a series of stills played one after another. Not necessarily a dynamic effect.

    Update: Never mind, the video DrJokepu posted proved it's dynamically generated.

    0 讨论(0)
  • 2020-11-28 01:27

    I know, I am a bit late with the answer, but Facebook has great library Shimmer that implements exactly that effect.

    0 讨论(0)
  • 2020-11-28 01:28

    I took the best from above solutions and created a neat method that does all for you:

    - (void)createSlideToUnlockViewWithText:(NSString *)text
    {
        UILabel *label = [[UILabel alloc] init];
        label.text = text;
        [label sizeToFit];
        label.textColor = [UIColor whiteColor];
    
        //Create an image from the label
        UIGraphicsBeginImageContextWithOptions(label.bounds.size, NO, 0.0);
        [[label layer] renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *textImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        CGFloat textWidth = textImage.size.width;
        CGFloat textHeight = textImage.size.height;
    
        CALayer *textLayer = [CALayer layer];
        textLayer.contents = (id)[textImage CGImage];
        textLayer.frame = CGRectMake(self.view.frame.size.width / 2 - textWidth / 2, self.view.frame.size.height / 2 - textHeight / 2, textWidth, textHeight);
    
        UIImage *maskImage = [UIImage imageNamed:@"Mask.png"];
        CALayer *maskLayer = [CALayer layer];
        maskLayer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.15] CGColor];
        maskLayer.contents = (id)maskImage.CGImage;
        maskLayer.contentsGravity = kCAGravityCenter;
        maskLayer.frame = CGRectMake(-textWidth - maskImage.size.width, 0.0, (textWidth * 2) + maskImage.size.width, textHeight);
    
        CABasicAnimation *maskAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
        maskAnimation.byValue = [NSNumber numberWithFloat:textWidth + maskImage.size.width];
        maskAnimation.repeatCount = HUGE_VALF;
        maskAnimation.duration = 2.0;
        maskAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
        [maskLayer addAnimation:maskAnimation forKey:@"slideAnimation"];
    
        textLayer.mask = maskLayer;
        self.slideToUnlockLayer = textLayer;
        [self.view.layer addSublayer:self.slideToUnlockLayer];
    }
    
    0 讨论(0)
  • 2020-11-28 01:30

    Thanks to rpetrich for the clipping gradient recipe. I am a newbie iPhone and Cocoa developer, so I was real happy to find it.

    I've implemented a decent-looking Slide to Cancel UIViewController using rpetrich's method. You can download the Xcode project of my implementation from here.

    My implementation uses a repeating NSTimer. I was unable to figure out how use Core (or Gore) Animation to have the iPhone's graphics engine continuously move the highlighting. I think that could be done on OS X with CALayer mask layers, but mask layers are not supported on iPhone OS.

    When I play with Apple's "Slide to Unlock" slider on my iPhone's home screen, I occasionally see the animation freeze. So I think Apple may be using a timer as well.

    If anyone can figure out how to do a non-timer based implementation using CA or OpenGL, I would love to see it.

    Thanks for the help!

    0 讨论(0)
  • 2020-11-28 01:33

    not so fresh...but maybe it'll be useful

    #define MM_TEXT_TO_DISPLAY          @"default"
    
    #define MM_FONT             [UIFont systemFontOfSize:MM_FONT_SIZE]
    #define MM_FONT_SIZE            25
    #define MM_FONT_COLOR           [[UIColor darkGrayColor] colorWithAlphaComponent:0.75f];
    
    #define MM_SHADOW_ENABLED           NO
    #define MM_SHADOW_COLOR         [UIColor grayColor]
    #define MM_SHADOW_OFFSET            CGSizeMake(-1,-1)
    
    
    #define MM_CONTENT_EDGE_INSETS_TOP      0
    #define MM_CONTENT_EDGE_INSETS_LEFT     10
    #define MM_CONTENT_EDGE_INSETS_BOTTON   0
    #define MM_CONTENT_EDGE_INSETS_RIGHT    10
    #define MM_CONTENT_EDGE_INSETS          UIEdgeInsetsMake(MM_CONTENT_EDGE_INSETS_TOP, MM_CONTENT_EDGE_INSETS_LEFT, MM_CONTENT_EDGE_INSETS_BOTTON, MM_CONTENT_EDGE_INSETS_RIGHT)
    
    #define MM_TEXT_ALIGNMENT           UITextAlignmentCenter
    #define MM_BACKGROUND_COLOR         [UIColor clearColor]
    
    #define MM_TIMER_INTERVAL           0.05f
    #define MM_HORIZONTAL_SPAN          5
    
    
    @interface MMAnimatedGradientLabel : UILabel {  
    
        NSString *textToDisplay;
        int text_length;
    
        CGGradientRef gradient;
    
        int current_position_x;
        NSTimer *timer;
    
        CGPoint alignment;
    
        CGGlyph *_glyphs;
    }
    
    - (id)initWithString:(NSString *)_string;
    
    - (void)startAnimation;
    - (void)toggle;
    - (BOOL)isAnimating;
    
    @end
    
    #define RGB_COMPONENTS(r, g, b, a)  (r) / 255.0f, (g) / 255.0f, (b) / 255.0f, (a)
    
    @interface MMAnimatedGradientLabel (Private)
    - (CGRect)calculateFrame;
    @end
    
    
    @implementation MMAnimatedGradientLabel
    
    // Missing in standard headers.
    extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);
    
    - (id)init {
        textToDisplay = MM_TEXT_TO_DISPLAY;
        return [self initWithFrame:[self calculateFrame]];
    }
    
    - (id)initWithString:(NSString *)_string {
        textToDisplay = _string;
        return [self initWithFrame:[self calculateFrame]];
    }
    
    -(id)initWithFrame:(CGRect)frame {  
        if (self = [super initWithFrame:frame]) {
    
            // set default values
            //
            self.textAlignment      = MM_TEXT_ALIGNMENT;
            self.backgroundColor    = MM_BACKGROUND_COLOR;
            self.font               = MM_FONT;
            self.text               = textToDisplay;
            self.textColor          = MM_FONT_COLOR;
    
            if (MM_SHADOW_ENABLED) {
                self.shadowColor        = MM_SHADOW_COLOR;
                self.shadowOffset       = MM_SHADOW_OFFSET;
            }
    
            text_length = -1;
    
            CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
            CGFloat colors[] =
            {       
                RGB_COMPONENTS(255.0, 255.0, 255.0, 0.00),
    //          RGB_COMPONENTS(255.0, 255.0, 255.0, 0.15),
                RGB_COMPONENTS(255.0, 255.0, 255.0, 0.95),
    //          RGB_COMPONENTS(255.0, 255.0, 255.0, 0.15),
                RGB_COMPONENTS(255.0, 255.0, 255.0, 0.00)
            };
    
            gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
            CGColorSpaceRelease(rgb);
    
            current_position_x = -(frame.size.width/2);// - MM_CONTENT_EDGE_INSETS.left - MM_CONTENT_EDGE_INSETS.right); 
        }
    
        return self;
    }
    
    - (CGRect)calculateFrame {
        CGSize size = [textToDisplay sizeWithFont:MM_FONT];
        NSLog(@"size: %f, %f", size.width, size.height);
        return CGRectMake(0, 0, size.width + MM_CONTENT_EDGE_INSETS.left + MM_CONTENT_EDGE_INSETS.right, size.height + MM_CONTENT_EDGE_INSETS.top + MM_CONTENT_EDGE_INSETS.bottom);
    }
    
    - (void)tick:(NSTimer*)theTimer {
        if (current_position_x < self.frame.size.width)
            current_position_x = current_position_x + MM_HORIZONTAL_SPAN;
        else
            current_position_x = -(self.frame.size.width/2); // - MM_CONTENT_EDGE_INSETS.left - MM_CONTENT_EDGE_INSETS.right);
    
        [self setNeedsDisplay]; 
    }
    
    - (void)startAnimation {    
        timer = [[NSTimer alloc] initWithFireDate:[NSDate date] 
                                         interval:MM_TIMER_INTERVAL
                                           target:self 
                                         selector:@selector(tick:)
                                         userInfo:nil
                                          repeats:YES];
    
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    }
    
    - (void)toggle {
    
        if (!timer) {
            timer = [[NSTimer alloc] initWithFireDate:[NSDate date] 
                                             interval:MM_TIMER_INTERVAL
                                               target:self 
                                             selector:@selector(tick:)
                                             userInfo:nil
                                              repeats:YES];
    
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        } else {
            [timer invalidate];
            [timer release];
            timer = nil;
    
            current_position_x = -(self.frame.size.width/2);
            [self setNeedsDisplay]; 
        }
    }
    
    - (BOOL)isAnimating {
    
        if (timer) 
            return YES;
        else
            return NO;
    }
    
    - (void)drawRect:(CGRect)rect {
        CGContextRef ctx = UIGraphicsGetCurrentContext();
    
        // Get drawing font.
        CGFontRef font = CGFontCreateWithFontName((CFStringRef)[[self font] fontName]);
        CGContextSetFont(ctx, font);
        CGContextSetFontSize(ctx, [[self font] pointSize]);
    
        // Calculate text drawing point only first time
        // 
        if (text_length == -1) {    
    
            // Transform text characters to unicode glyphs.
            text_length = [[self text] length];
            unichar chars[text_length];
            [[self text] getCharacters:chars range:NSMakeRange(0, text_length)];
    
            _glyphs = malloc(sizeof(CGGlyph) * text_length);
            for (int i=0; i<text_length;i ++)
                _glyphs[i] = chars[i] - 29;
    
            // Measure text dimensions.
            CGContextSetTextDrawingMode(ctx, kCGTextInvisible); 
            CGContextSetTextPosition(ctx, 0, 0);
            CGContextShowGlyphs(ctx, _glyphs, text_length);
            CGPoint textEnd = CGContextGetTextPosition(ctx);
    
            // Calculate text drawing point.        
            CGPoint anchor = CGPointMake(textEnd.x * (-0.5), [[self font] pointSize] * (-0.25));  
            CGPoint p = CGPointApplyAffineTransform(anchor, CGAffineTransformMake(1, 0, 0, -1, 0, 1));
    
            if ([self textAlignment] == UITextAlignmentCenter) 
                alignment.x = [self bounds].size.width * 0.5 + p.x;
            else if ([self textAlignment] == UITextAlignmentLeft) 
                alignment.x = 0;
            else 
                alignment.x = [self bounds].size.width - textEnd.x;
    
            alignment.y = [self bounds].size.height * 0.5 + p.y;
        }
    
        // Flip back mirrored text.
        CGContextSetTextMatrix(ctx, CGAffineTransformMakeScale(1, -1));
    
        // Draw shadow.
        CGContextSaveGState(ctx);
        CGContextSetTextDrawingMode(ctx, kCGTextFill);
        CGContextSetFillColorWithColor(ctx, [[self textColor] CGColor]);
        CGContextSetShadowWithColor(ctx, [self shadowOffset], 0, [[self shadowColor] CGColor]);
        CGContextShowGlyphsAtPoint(ctx, alignment.x, alignment.y, _glyphs, text_length);
        CGContextRestoreGState(ctx);
    
        // Draw text clipping path.
        CGContextSetTextDrawingMode(ctx, kCGTextClip);
        CGContextShowGlyphsAtPoint(ctx, alignment.x, alignment.y, _glyphs, text_length);
    
        // Restore text mirroring.
        CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
    
        if ([self isAnimating]) {
            // Fill text clipping path with gradient.
            CGPoint start = CGPointMake(rect.origin.x + current_position_x, rect.origin.y);
            CGPoint end = CGPointMake(rect.size.width/3*2 + current_position_x, rect.origin.y);
    
            CGContextDrawLinearGradient(ctx, gradient, start, end, 0);
        }
    }
    
    
    - (void) dealloc {
        free(_glyphs);
        [timer invalidate];
        [timer release];
    
        CGGradientRelease(gradient);
        [super dealloc];
    }
    
    0 讨论(0)
  • 2020-11-28 01:33

    I uploaded to GitHub a mini project who helps with the “slide to unlock” animation.

    https://github.com/GabrielMassana/GM_FSHighlightAnimationAdditions

    The project have LTR, RTL, Up to Down and Down to Up animations and it is based in the posts:

    Pascal Bourque: https://stackoverflow.com/a/2778232/1381708

    cberkley: https://stackoverflow.com/a/5710097/1381708

    Cheers

    0 讨论(0)
提交回复
热议问题