iPhone“滑动解锁”animation

关于苹果如何实现“幻灯片解锁”的任何想法(也就是另一个相同的例子)animation?

我想过某种animation蒙版 – 但是由于性能的原因,在iPhone OS上不提供蒙版。

是否有一些私人API的效果(如SuckEffect),他们可能使用? 聚光灯效果types? 一些核心animation的东西?

编辑:这绝对不是一系列的剧照。 我已经看到了编辑一个plist值的例子,并在jailbreak的iphones上自定义string。

通过使用核心animation可以轻松完成,在显示文本的图层上为遮罩层设置animation。

试试这个在任何普通的UIViewController(你可以从一个基于视图的应用程序项目模板的新的Xcode项目开始),或者抓住我的Xcode项目:

请注意, CALayer.mask属性仅在iPhone OS 3.0及更高版本中可用。

 - (void)viewDidLoad { self.view.layer.backgroundColor = [[UIColor blackColor] CGColor]; UIImage *textImage = [UIImage imageNamed:@"SlideToUnlock.png"]; CGFloat textWidth = textImage.size.width; CGFloat textHeight = textImage.size.height; CALayer *textLayer = [CALayer layer]; textLayer.contents = (id)[textImage CGImage]; textLayer.frame = CGRectMake(10.0f, 215.0f, textWidth, textHeight); CALayer *maskLayer = [CALayer layer]; // Mask image ends with 0.15 opacity on both sides. Set the background color of the layer // to the same value so the layer can extend the mask image. maskLayer.backgroundColor = [[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.15f] CGColor]; maskLayer.contents = (id)[[UIImage imageNamed:@"Mask.png"] CGImage]; // Center the mask image on twice the width of the text layer, so it starts to the left // of the text layer and moves to its right when we translate it by width. maskLayer.contentsGravity = kCAGravityCenter; maskLayer.frame = CGRectMake(-textWidth, 0.0f, textWidth * 2, textHeight); // Animate the mask layer's horizontal position CABasicAnimation *maskAnim = [CABasicAnimation animationWithKeyPath:@"position.x"]; maskAnim.byValue = [NSNumber numberWithFloat:textWidth]; maskAnim.repeatCount = HUGE_VALF; maskAnim.duration = 1.0f; [maskLayer addAnimation:maskAnim forKey:@"slideAnim"]; textLayer.mask = maskLayer; [self.view.layer addSublayer:textLayer]; [super viewDidLoad]; } 

这个代码使用的图像是:

遮罩层 文本图层

还有另一种使用图层蒙版的解决scheme,而是用手绘制渐变,而不需要图像。 视图是带有animation的视图,透明度是从0 – 1定义透明度的浮点数(1 =没有无意义的透明度),gradientWidth是渐变的所需宽度。

 CAGradientLayer *gradientMask = [CAGradientLayer layer]; gradientMask.frame = view.bounds; CGFloat gradientSize = gradientWidth / view.frame.size.width; UIColor *gradient = [UIColor colorWithWhite:1.0f alpha:transparency]; NSArray *startLocations = @[[NSNumber numberWithFloat:0.0f], [NSNumber numberWithFloat:(gradientSize / 2)], [NSNumber numberWithFloat:gradientSize]]; NSArray *endLocations = @[[NSNumber numberWithFloat:(1.0f - gradientSize)], [NSNumber numberWithFloat:(1.0f -(gradientSize / 2))], [NSNumber numberWithFloat:1.0f]]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"locations"]; gradientMask.colors = @[(id)gradient.CGColor, (id)[UIColor whiteColor].CGColor, (id)gradient.CGColor]; gradientMask.locations = startLocations; gradientMask.startPoint = CGPointMake(0 - (gradientSize * 2), .5); gradientMask.endPoint = CGPointMake(1 + gradientSize, .5); view.layer.mask = gradientMask; animation.fromValue = startLocations; animation.toValue = endLocations; animation.repeatCount = HUGE_VALF; animation.duration = 3.0f; [gradientMask addAnimation:animation forKey:@"animateGradient"]; 

SWIFT版本:

 let transparency:CGFloat = 0.5 let gradientWidth: CGFloat = 40 let gradientMask = CAGradientLayer() gradientMask.frame = swipeView.bounds let gradientSize = gradientWidth/swipeView.frame.size.width let gradient = UIColor(white: 1, alpha: transparency) let startLocations = [0, gradientSize/2, gradientSize] let endLocations = [(1 - gradientSize), (1 - gradientSize/2), 1] let animation = CABasicAnimation(keyPath: "locations") gradientMask.colors = [gradient.CGColor, UIColor.whiteColor().CGColor, gradient.CGColor] gradientMask.locations = startLocations gradientMask.startPoint = CGPointMake(0 - (gradientSize*2), 0.5) gradientMask.endPoint = CGPointMake(1 + gradientSize, 0.5) swipeView.layer.mask = gradientMask animation.fromValue = startLocations animation.toValue = endLocations animation.repeatCount = HUGE animation.duration = 3 gradientMask.addAnimation(animation, forKey: "animateGradient") 

Swift 3

 fileprivate func addGradientMaskToView(view:UIView, transparency:CGFloat = 0.5, gradientWidth:CGFloat = 40.0) { let gradientMask = CAGradientLayer() gradientMask.frame = view.bounds let gradientSize = gradientWidth/view.frame.size.width let gradientColor = UIColor(white: 1, alpha: transparency) let startLocations = [0, gradientSize/2, gradientSize] let endLocations = [(1 - gradientSize), (1 - gradientSize/2), 1] let animation = CABasicAnimation(keyPath: "locations") gradientMask.colors = [gradientColor.cgColor, UIColor.white.cgColor, gradientColor.cgColor] gradientMask.locations = startLocations as [NSNumber]? gradientMask.startPoint = CGPoint(x:0 - (gradientSize * 2), y: 0.5) gradientMask.endPoint = CGPoint(x:1 + gradientSize, y: 0.5) view.layer.mask = gradientMask animation.fromValue = startLocations animation.toValue = endLocations animation.repeatCount = HUGE animation.duration = 3 gradientMask.add(animation, forKey: nil) } 

您可以使用kCGTextClip绘图模式设置剪切path,然后填充渐变。

 // Get Context CGContextRef context = UIGraphicsGetCurrentContext(); // Set Font CGContextSelectFont(context, "Helvetica", 24.0, kCGEncodingMacRoman); // Set Text Matrix CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0); CGContextSetTextMatrix(context, xform); // Set Drawing Mode to set clipping path CGContextSetTextDrawingMode (context, kCGTextClip); // Draw Text CGContextShowTextAtPoint (context, 0, 20, "Gradient", strlen("Gradient")); // Calculate Text width CGPoint textEnd = CGContextGetTextPosition(context); // Generate Gradient locations & colors size_t num_locations = 3; CGFloat locations[3] = { 0.3, 0.5, 0.6 }; CGFloat components[12] = { 1.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, }; // Load Colorspace CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); // Create Gradient CGGradientRef gradient = CGGradientCreateWithColorComponents (colorspace, components, locations, num_locations); // Draw Gradient (using clipping path CGContextDrawLinearGradient (context, gradient, rect.origin, textEnd, 0); // Cleanup (exercise for reader) 

设置一个NSTimer,并改变位置的值,或使用CoreAnimation来做同样的事情。

我将Pascal上面提供的代码添加为UILabel上的一个类别,以便您可以以此方式为任何UILabel设置animation。 这是代码。 有些参数可能需要改变你的背景颜色等。它使用帕斯卡已经在他的答案embedded相同的面具图像。

 //UILabel+FSHighlightAnimationAdditions.m #import "UILabel+FSHighlightAnimationAdditions.h" #import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> @implementation UILabel (FSHighlightAnimationAdditions) - (void)setTextWithChangeAnimation:(NSString*)text { NSLog(@"value changing"); self.text = text; CALayer *maskLayer = [CALayer layer]; // Mask image ends with 0.15 opacity on both sides. Set the background color of the layer // to the same value so the layer can extend the mask image. maskLayer.backgroundColor = [[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.15f] CGColor]; maskLayer.contents = (id)[[UIImage imageNamed:@"Mask.png"] CGImage]; // Center the mask image on twice the width of the text layer, so it starts to the left // of the text layer and moves to its right when we translate it by width. maskLayer.contentsGravity = kCAGravityCenter; maskLayer.frame = CGRectMake(self.frame.size.width * -1, 0.0f, self.frame.size.width * 2, self.frame.size.height); // Animate the mask layer's horizontal position CABasicAnimation *maskAnim = [CABasicAnimation animationWithKeyPath:@"position.x"]; maskAnim.byValue = [NSNumber numberWithFloat:self.frame.size.width]; maskAnim.repeatCount = 1e100f; maskAnim.duration = 2.0f; [maskLayer addAnimation:maskAnim forKey:@"slideAnim"]; self.layer.mask = maskLayer; } @end //UILabel+FSHighlightAnimationAdditions.h #import <Foundation/Foundation.h> @interface UILabel (FSHighlightAnimationAdditions) - (void)setTextWithChangeAnimation:(NSString*)text; @end 

不是那么新鲜…但也许它会有用的

 #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 + px; 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 + py; } // 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]; } 

感谢rpetrich为剪切梯度配方。 我是一个新手iPhone和cocoa开发人员,所以我很高兴find它。

我已经实现了一个像样的幻灯片来取消 UIViewController使用rpetrich的方法。 你可以从这里下载我的实现的Xcode项目。

我的实现使用重复的NSTimer。 我无法弄清楚如何使用核心(或戈尔)animation来让iPhone的graphics引擎不断移动突出显示。 我认为可以在OS X上使用CALayer遮罩层完成,但是iPhone OS不支持遮罩层。

当我在iPhone的主屏幕上玩苹果的“滑动解锁”滑块时,偶尔会看到animation冻结。 所以我认为苹果也可能会使用一个计时器。

如果任何人可以弄清楚如何使用CA或OpenGL做非基于计时器的实现,我很乐意看到它。

谢谢您的帮助!

我知道,我的答案有点迟,但是Facebook有一个很好的库Shimmer ,它实现了这个效果。

我上传到GitHub一个小型项目谁帮助“幻灯片解锁”animation。

https://github.com/GabrielMassana/GM_FSHighlightAnimationAdditions

该项目有LTR,RTL,直到animation和羽绒animation,它是基于在职位:

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

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

干杯

我从上面的解决scheme中获得了最好的结果,并创build了一个完整的方法,

 - (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]; } 

首先,非常感谢marcio为他的解决scheme。 这几乎完美的工作,节省了我几个小时的努力,并在我的应用程序做了一个巨大的飞溅。 我的老板喜欢它。 我欠你啤酒。 或者几个。

只有一个小的更正iPhone 4。 我的意思是硬件本身,而不仅仅是iOS 4.他们将iPhone 4上的系统字体从Helvetica(iPhone 3Gs及以下版本)更改为Helvetic Neue。 这导致你正在从字符到字形的转换正好是closures的4个点。 例如,string“fg”将显示为“bc”。 我通过明确地将字体设置为“Helvetica”而不是使用“systemFontofSize”来解决这个问题。 现在它像一个魅力。

再次…谢谢!

也许这只是一个渲染出来的animation – 你知道,一系列的剧照依次播放。 不一定是一个dynamic的效果。

更新:没关系,DrJokepu发布的videocertificate了它是dynamic生成的。

  • 顶部:具有不透明背景和明文的UILabel
    • 清除文本通过复杂的遮罩过程在drawRect:func中呈现
  • 中间:工作人员视图执行重复animation,将图像移到顶部标签后面
  • 底部:一个UIView,您可以按顺序添加中间和顶部的子视图。 可以是任何你想要的文字颜色

一个例子可以在这里看到https://github.com/jhurray/AnimatedLabelExample