iPhone "슬라이드하여 잠금 해제"애니메이션
Apple이 "슬라이드하여 잠금 해제"(또한 "슬라이드하여 전원 끄기"도 동일한 예) 애니메이션을 구현 한 방법에 대한 아이디어가 있습니까?
나는 일종의 애니메이션 마스크에 대해 생각했지만 성능상의 이유로 iPhone OS에서는 마스킹을 사용할 수 없습니다.
사용했을 수있는 비공개 API 효과 (예 : SuckEffect)가 있습니까? 스포트라이트 유형의 효과? 핵심 애니메이션 문제?
편집 : 확실히 일련의 스틸이 아닙니다. 나는 plist 값이나 무언가를 편집하고 탈옥 아이폰에서 문자열을 사용자 정의하는 예를 보았습니다.
Core Animation을 사용하여 텍스트를 표시하는 레이어의 마스크 레이어에 애니메이션을 적용하여 쉽게 수행 할 수 있습니다.
일반 UIViewController ( View 기반 애플리케이션 프로젝트 템플릿을 기반 으로하는 새로운 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];
}
이 코드에서 사용하는 이미지는 다음과 같습니다.
레이어 마스크를 사용하는 또 다른 솔루션이지만 대신 손으로 그라디언트를 그리고 이미지가 필요하지 않습니다. 보기는 애니메이션이있는보기이고, 투명도는 투명도 (1 = 무의미한 투명도 없음)를 정의하는 0-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")
스위프트 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
클리핑 경로를 설정 그리기 모드를 다음 그라데이션 채우기.
// 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을 사용하여 동일하게 수행하십시오.
UILabel의 카테고리로 Pascal에서 제공 한 코드를 추가하여 이러한 방식으로 모든 UILabel을 애니메이션 할 수 있습니다. 여기에 코드가 있습니다. 배경색 등을 위해 일부 매개 변수를 변경해야 할 수도 있습니다. Pascal이 답변에 삽입 한 것과 동일한 마스크 이미지를 사용합니다.
//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 + 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];
}
클리핑 그래디언트 레시피에 대해 rpetrich에게 감사드립니다. 저는 초보 iPhone 및 Cocoa 개발자이기 때문에 정말 기뻤습니다.
rpetrich의 방법을 사용하여 UIViewController 를 취소 하는 괜찮은 모양의 슬라이드를 구현했습니다 . 여기 에서 내 구현의 Xcode 프로젝트를 다운로드 할 수 있습니다 .
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!
I know, I am a bit late with the answer, but Facebook has great library Shimmer that implements exactly that effect.
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];
}
First, a HUGE thank you to marcio for his solution. This worked almost perfectly, saved me hours of effort, and made a huge splash in my app. My boss loved it. I owe you beer. Or several.
One small correction for iPhone 4 only. I mean the hardware itself, not just iOS 4. They changed the system font on the iPhone 4 from Helvetica (iPhone 3Gs and below) to Helvetic Neue. This caused the translation you're doing from character to glyphs to be off by exactly 4 spots. For example the string "fg" would appear as "bc". I fixed this by explicitly setting the font to "Helvetica" rather than using "systemFontofSize". Now it works like a charm.
Again...THANK YOU!
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
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.
- Top: UILabel with opaque background and clear text
- Clear text is rendered in drawRect: func through complicated masking process
- Middle: Worker View that is performing a repeating animation moving an image behind the top label
- Bottom: a UIView that you add the middle and top subview to in that order. Can be whatever color you want the text to be
https://github.com/jhurray/AnimatedLabelExample에서 예제를 볼 수 있습니다.
참고 URL : https://stackoverflow.com/questions/438046/iphone-slide-to-unlock-animation
'code' 카테고리의 다른 글
Python : 특정 버전 2.4.9로 opencv2를 pip 설치하는 방법은 무엇입니까? (0) | 2020.10.26 |
---|---|
axios POST 요청으로 헤더 전달 (0) | 2020.10.26 |
Xcode 4에서 .h와 .m 사이를 전환하는 방법 (0) | 2020.10.26 |
UIWebView에 대해 sharedHTTPCookieStorage를 사용하여 쿠키를 수동으로 설정할 수 있습니까? (0) | 2020.10.26 |
SQL Server 2005, 날짜 조작에서 시간 차이를 찾기 위해 두 날짜를 비교하는 방법 (0) | 2020.10.26 |