Анімація iPhone “розсуньте, щоб розблокувати”


75

Будь-які ідеї щодо того, як Apple реалізувала анімацію "слайд для розблокування" (також "слайд для вимкнення" - ще один ідентичний приклад)?

Я думав про якусь анімаційну маску, але маскування недоступне в ОС iPhone з міркувань продуктивності.

Чи існує якийсь приватний ефект API (наприклад, SuckEffect), який вони могли використовувати? Ефект типу прожектора? Якась основна анімація?

Редагувати: Це точно не серія кадрів. Я бачив приклади редагування значення plist чи чогось іншого та налаштування рядка на зламаних iphone.


1
Чи правильно я розумію, що iPhone не підтримує прозорість? (У мене немає, тому я не знаю). У будь-якому випадку, згідно з цим відео, фактичний текст можна змінити, тому це не якась попередньо відтворена анімація: uk.youtube.com/watch?v=PVhRnL55cJQ
Тамаш

Це розбитий телефон. Я хотів би знати, як використовувати цю анімацію за допомогою офіційного SDK. Тобто, без використання прихованих API або доступу до речей, вилучених з бета-версій, таких як CoreFilters тощо.

Відповіді:


70

Це можна легко зробити за допомогою Core Animation, анімуючи шар маски на шарі, що відображає текст.

Спробуйте це в будь-якому простому UIViewController (ви можете розпочати з нового проекту Xcode, заснованого на шаблоні проекту програми на основі View ), або захопіть мій проект 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];
}

Зображення, використовувані цим кодом:

Масковий шар Шар тексту


2
Чудове рішення. Для тих, хто зацікавлений не додавати ресурс для етикетки, я створив UIImageз UILabel(або будь-якого іншого UIView): UIGraphicsBeginImageContext(_label); [[_label layer] renderInContext:UIGraphicsGetCurrentContext()]; UIImage *textImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
Мауріціо

50

Ще одне рішення з використанням маски шару, але замість цього малює градієнт вручну і не вимагає зображень. Перегляд - це вигляд з анімацією, прозорість - це плаваюча величина від 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")

Стрімкий 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)
}

3
Працює як шарм! Вам слід замінити константу repeatCount на HUGE_VALF, як пропонують документи.
MBulli

Найкраще! Дуже просто застосовувати до будь-якого виду CALayer! Зробив для цього метод: - (void) addSlidingTextEffectToLabel: (UILabel *) напрямок мітки: (UISwipeGestureRecognizerDirection) dir gradientWidth: (CGFloat) gradientWidth speed: (CGFloat) dur intenzitet: (CGFloat) intenzitet;
Леонард Паулі,

1
@Samsinite одне питання, навіщо вам потрібно [view removeFromSuperview]перед тим, як встановити шар маски і [superview addSubview:view]після? Здається, і без цього нормально працює, може, чогось мені не вистачає?
dariaa

@dariaa, Минув більше року з тих пір, як я зробив будь-яку розробку iOS, тому моя пам’ять хитка. Але, схоже, він би намалював градієнтну маску лише тоді, коли він буде доданий до батьківського подання. Після того, як його вже було додано до батьківського подання (намальовано), будь-які додані маски шару не будуть намальовані. Це могло змінитися дотепер, хоча цей код був написаний протягом iOS 6 днів.
Самсініте

Хороший! Дякую, що поділились.
Томас Калмон,

13

Ви можете використовувати 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, щоб зробити те саме.


5
треба любити GoreAnimationв кінці
Річард Дж. Росс III

3
Думав, коли хтось це підбере. Зайняв майже рік.
rpetrich

@Soonts: вам може знадобитися використовувати kCGEncodingFontSpecificзамість цього
rpetrich

@rpetrich: він повністю зламаний. AFAIK, для kCGEncodingFontSpecific я повинен надати власні переклади символів у гліфи, і в iOS для цього немає API.
Soonts

12

Я додав код, наданий вище Паскалем, як категорію на UILabel, щоб ви могли анімувати будь-який UILabel таким чином. Ось код. Деякі параметри, можливо, доведеться змінити для кольорів тла тощо. Він використовує те саме зображення маски, яке Паскаль вклав у свою відповідь.

//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

1
Це не працює для мене нестандартно ... можливо, я пропускаю щось конкретне з конфігурацією мого UILabel. Повноцінний приклад, як це зробити без створення образу мого тексту, було б чудово.
bandejapaisa

@bandejapaisa Ви, мабуть, не додали Mask.png до своїх ресурсів
Mazyod

Краще використовувати FLT_MAXзамість 1e100f.
Пор.

5

не настільки свіжий ... але, можливо, це буде корисно

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

CGFontGetGlyphsForUnichars - деякі люди, які використовують цей звіт, не проходять перевірку щодо використання приватного API?
Soonts

4

Дякую rpetrich за рецепт відсікання градієнта. Я новачок для iPhone та розробника какао, тому був дуже радий знайти це.

Я застосував гідний на вигляд слайд для скасування UIViewController, використовуючи метод rpetrich. Ви можете завантажити проект Xcode моєї реалізації звідси .

У моїй реалізації використовується повторюваний NSTimer. Мені не вдалося зрозуміти, як використовувати Core (або Gore) Animation, щоб графічний движок iPhone постійно переміщував виділення. Я думаю, що це можна зробити на OS X із шарами маски CALayer, але шари маски не підтримуються в ОС iPhone.

Коли я граю за допомогою повзунка Apple "Slide to Unlock" на головному екрані свого iPhone, я іноді бачу, як анімація зависає. Тому я думаю, що Apple, можливо, також використовує таймер.

Якщо хтось може зрозуміти, як виконати реалізацію, яка не виконується за допомогою таймера, за допомогою CA або OpenGL, я хотів би це побачити.

Дякую за допомогу!



2

Я взяв найкраще з наведених вище рішень і створив акуратний метод, який робить все для вас:

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

1

По-перше, ВЕЛИЧЕЗНА подяка марсіо за його рішення. Це спрацювало майже ідеально, заощадило мені години зусиль і викликало величезний сплеск у моєму додатку. Мій начальник це любив. Я винен тобі пивом. Або кілька.

Одна невеличка корекція лише для iPhone 4. Я маю на увазі саме обладнання, а не лише iOS 4. Вони змінили системний шрифт на iPhone 4 з Helvetica (iPhone 3G і нижче) на Helvetic Neue. Це призвело до того, що переклад, який ви робите з символу на гліфи, відключився рівно на 4 місця. Наприклад, рядок "fg" буде виглядати як "bc". Я виправив це, явно встановивши шрифт на "Helvetica", а не використовуючи "systemFontofSize". Зараз це працює як шарм.

Ще раз ... ДЯКУЮ!


1

Я завантажив на GitHub міні-проект, який допомагає з анімацією "розблокувати слайд".

https://github.com/GabrielMassana/GM_FSHighlightAnimationAdditions

Проект має анімацію LTR, RTL, Up up Down і Down Up Up, і він базується на публікаціях:

Паскаль Бурке: https://stackoverflow.com/a/2778232/1381708

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

Ура


1

Ось версія SwiftUI:

struct Shimmer: AnimatableModifier {

    private let gradient: Gradient

    init(sideColor: Color = Color(white: 0.25), middleColor: Color = .white) {
        gradient = Gradient(colors: [sideColor, middleColor, sideColor])
    }

    @State private var position: CGFloat = 0
    var animatableData: CGFloat {
        get { position }
        set { position = newValue }
    }

    func body(content: Content) -> some View {
        content
            .overlay(LinearGradient(
                        gradient: gradient,
                        startPoint: .init(x: position - 0.2 * (1 - position), y: 0.5),
                        endPoint: .init(x: position + 0.2 * position, y: 0.5)))
            .mask(content)
            .onAppear {
                withAnimation(Animation
                                .linear(duration: 2)
                                .delay(1)
                                .repeatForever(autoreverses: false)) {
                    position = 1
                }
            }
    }
}

Використовуйте його так:

Text("slide to unlock")
    .modifier(Shimmer())

0

Можливо, це просто відтворена анімація - знаєте, серія кадрів відтворюється одна за одною. Не обов'язково динамічний ефект.

Оновлення: Неважливо, опубліковане відео DrJokepu доводить, що воно динамічно генерується.


0
  • Вгорі: UILabel з непрозорим фоном і чітким текстом
    • Чистий текст відображається у drawRect: func через складний процес маскування
  • Середній: робочий вигляд, який виконує повторювану анімацію, рухаючи зображення за верхньою міткою
  • Знизу: UIV Перегляньте, що ви додаєте середній і верхній підвигляд у такому порядку. Може бути будь-якого кольору, яким ви хочете, щоб був текст

Приклад можна побачити тут https://github.com/jhurray/AnimatedLabelExample

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.