iPhone "Animation zum Entsperren"

74

Irgendwelche Ideen, wie Apple die Animation "Folie zum Entsperren" implementiert hat (auch "Folie zum Ausschalten" ist ein weiteres identisches Beispiel)?

Ich habe über eine Art Animationsmaske nachgedacht - aber die Maskierung ist auf dem iPhone OS aus Leistungsgründen nicht verfügbar.

Gibt es einen privaten API-Effekt (wie SuckEffect), den sie möglicherweise verwendet haben? Ein Spotlight-Effekt? Eine Kernanimationssache?

Edit: Es ist definitiv keine Serie von Standbildern. Ich habe Beispiele gesehen, wie man einen Plist-Wert oder etwas bearbeitet und die Zeichenfolge auf iphone mit Jailbreak anpasst.

Gabriel.Massana
quelle
1
Verstehe ich richtig, dass das iPhone keine Transparenz unterstützt? (Ich besitze keine, also weiß ich es nicht) Laut diesem Video kann der tatsächliche Text geändert werden, sodass es sich nicht um eine vorgerenderte Animation handelt: uk.youtube.com/watch?v=PVhRnL55cJQ
Tamas Czinege
Dies ist ein Telefon mit Jailbreak. Ich wollte wissen, wie man diese Animation mit dem offiziellen SDK abruft. Das heißt, ohne versteckte APIs zu verwenden oder auf Dinge zuzugreifen, die aus Beta-Versionen wie CoreFilters und dergleichen entfernt wurden.

Antworten:

70

Dies kann einfach mithilfe der Kernanimation erfolgen, bei der eine Maskenebene auf der Ebene animiert wird, auf der der Text angezeigt wird.

Versuchen Sie, diese in jeder Ebene UIViewController (können Sie mit einem neuen Xcode - Projekt starten , basierend auf der View-basierte Anwendung Projektvorlage) oder mein Xcode - Projekt greifen hier :

Beachten Sie, dass die CALayer.maskEigenschaft nur in iPhone OS 3.0 und höher verfügbar ist.

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

Die von diesem Code verwendeten Bilder sind:

Maskenebene Textebene

Pascal Bourque
quelle
2
Tolle Lösung. Für diejenigen, die daran interessiert sind, keine Ressource für das Label hinzuzufügen, habe ich eine UIImageaus einem UILabel(oder einer anderen UIView) erstellt: UIGraphicsBeginImageContext(_label); [[_label layer] renderInContext:UIGraphicsGetCurrentContext()]; UIImage *textImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
Maurizio
50

Noch eine andere Lösung, die eine Ebenenmaske verwendet, aber stattdessen den Farbverlauf von Hand zeichnet und keine Bilder benötigt. Ansicht ist die Ansicht mit der Animation, Transparenz ist ein Gleitkommawert von 0 bis 1, der den Transparenzbetrag definiert (1 = keine Transparenz, die sinnlos ist), und gradientWidth ist die gewünschte Breite des Verlaufs.

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"];

SCHNELLE VERSION:

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)
}
Samsinite
quelle
3
Klappt wunderbar! Sie sollten die repeatCount-Konstante durch HUGE_VALF ersetzen, wie in den Dokumenten vorgeschlagen.
MBulli
Beste! Super einfach auf jede Art von CALayer anzuwenden! Eine Methode dafür erstellt: - (void) addSlidingTextEffectToLabel: (UILabel *) Beschriftungsrichtung: (UISwipeGestureRecognizerDirection) dir gradientWidth: (CGFloat) gradientWidth speed: (CGFloat) dur Intensität: (CGFloat) Intensität;
Leonard Pauli
1
@ Samsinite eine Frage, warum brauchst du, [view removeFromSuperview]bevor du eine Maske auflegst und [superview addSubview:view]danach? Scheint ohne zu funktionieren, kann es sein, dass mir etwas fehlt?
Dariaa
@dariaa, es ist über ein Jahr her, seit ich einen iOS-Entwickler gemacht habe, also ist mein Gedächtnis wackelig. Es sieht jedoch so aus, als würde die Verlaufsmaske nur gezeichnet, wenn sie der übergeordneten Ansicht hinzugefügt wird. Sobald es bereits zu einer übergeordneten Ansicht hinzugefügt wurde (gezeichnet), werden keine hinzugefügten Ebenenmasken mehr gezeichnet. Dies könnte sich inzwischen geändert haben, dieser Code wurde während der iOS 6 Tage geschrieben.
Samsinite
Schön! Danke für das Teilen.
Thomás Calmon
13

Im kCGTextClipZeichenmodus können Sie den Beschneidungspfad festlegen und dann mit einem Farbverlauf füllen.

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

Richten Sie einen NSTimer ein und variieren Sie die Werte an den Standorten, oder verwenden Sie CoreAnimation, um dasselbe zu tun.

rpetrich
quelle
5
Ich muss das GoreAnimationam Ende lieben
Richard J. Ross III
3
Ich habe mich gefragt, wann jemand das aufgreifen würde. Hat fast ein Jahr gedauert.
rpetrich
@Soonts: Möglicherweise müssen Sie kCGEncodingFontSpecificstattdessen verwenden
rpetrich
@rpetrich: Es ist völlig kaputt. AFAIK, für kCGEncodingFontSpecific muss ich meine eigenen Übersetzungen von Zeichen zu Glyphen bereitstellen, und dafür gibt es in iOS keine API.
Bald
12

Ich habe den oben von Pascal bereitgestellten Code als Kategorie für UILabel hinzugefügt, damit Sie jedes UILabel auf diese Weise animieren können. Hier ist der Code. Einige Parameter müssen möglicherweise für Ihre Hintergrundfarben usw. geändert werden. Es wird dasselbe Maskenbild verwendet, das Pascal in seine Antwort eingebettet hat.

//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
Cberkley
quelle
Sehr hilfreich. Vielen Dank.
Sam
1
Dies funktioniert bei mir nicht sofort ... Vielleicht fehlt mir etwas Bestimmtes in der Konfiguration meines UILabels. Ein voll funktionsfähiges Beispiel dafür, ohne ein Bild meines Textes zu erstellen, wäre großartig.
Bandejapaisa
@bandejapaisa Sie haben wahrscheinlich Mask.png nicht zu Ihren Ressourcen hinzugefügt
Mazyod
Bessere Verwendung von FLT_MAXstatt 1e100f.
Vgl.
5

nicht so frisch ... aber vielleicht ist es nützlich

#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];
}
Marcio
quelle
CGFontGetGlyphsForUnichars - Einige Personen, die diesen Bericht verwenden, haben die Überprüfung für die Verwendung der privaten API nicht bestanden.
Soonts
4

Vielen Dank an rpetrich für das Rezept für den Schnittverlauf. Ich bin ein Neuling in der Entwicklung von iPhone und Kakao, daher war ich sehr glücklich, es zu finden.

Ich habe eine anständig aussehende Folie implementiert, um UIViewController mit der Methode von rpetrich abzubrechen. Sie können das Xcode-Projekt meiner Implementierung hier herunterladen .

Meine Implementierung verwendet einen sich wiederholenden NSTimer. Ich konnte nicht herausfinden, wie ich mithilfe der Core- (oder Gore-) Animation die Hervorhebung durch die Grafik-Engine des iPhones kontinuierlich verschieben kann. Ich denke, dass dies unter OS X mit CALayer-Maskenebenen möglich ist, aber Maskenebenen werden unter iPhone OS nicht unterstützt.

Wenn ich mit dem Schieberegler "Slide to Unlock" von Apple auf dem Startbildschirm meines iPhones spiele, wird die Animation gelegentlich eingefroren. Daher denke ich, dass Apple möglicherweise auch einen Timer verwendet.

Wenn jemand herausfinden kann, wie eine nicht auf Timern basierende Implementierung mit CA oder OpenGL durchgeführt wird, würde ich es gerne sehen.

Danke für die Hilfe!

MyCatsNameIsBernie
quelle
4

Ich weiß, ich bin etwas spät mit der Antwort, aber Facebook hat eine großartige Bibliothek Shimmer , die genau diesen Effekt implementiert.

Maxim Pawlow
quelle
2

Ich habe das Beste aus den oben genannten Lösungen genommen und eine saubere Methode entwickelt, die alles für Sie erledigt:

- (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];
}
Nico S.
quelle
1

Zunächst ein RIESIGES Dankeschön an Marcio für seine Lösung. Dies funktionierte fast perfekt, ersparte mir stundenlange Mühe und sorgte in meiner App für Furore. Mein Chef hat es geliebt. Ich schulde dir Bier. Oder mehrere.

Eine kleine Korrektur nur für iPhone 4. Ich meine die Hardware selbst, nicht nur iOS 4. Sie haben die Systemschriftart auf dem iPhone 4 von Helvetica (iPhone 3Gs und darunter) in Helvetic Neue geändert. Dies führte dazu, dass die Übersetzung, die Sie von Zeichen zu Glyphen ausführen, um genau 4 Punkte verschoben wurde. Zum Beispiel würde die Zeichenfolge "fg" als "bc" erscheinen. Ich habe dies behoben, indem ich die Schriftart explizit auf "Helvetica" gesetzt habe, anstatt "systemFontofSize" zu verwenden. Jetzt funktioniert es wie ein Zauber.

Nochmals vielen Dank!

Dave Klotz
quelle
0

Vielleicht ist es nur eine gerenderte Animation - Sie wissen, eine Reihe von Standbildern, die nacheinander abgespielt werden. Nicht unbedingt ein dynamischer Effekt.

Update: Egal, das von DrJokepu veröffentlichte Video hat bewiesen, dass es dynamisch generiert wird.

Bhollis
quelle
0
  • Oben: UILabel mit undurchsichtigem Hintergrund und klarem Text
    • Klartext wird in drawRect: func durch einen komplizierten Maskierungsprozess gerendert
  • Mitte: Worker View, die eine sich wiederholende Animation ausführt und ein Bild hinter das obere Etikett verschiebt
  • Unten: Eine UIView, zu der Sie die mittlere und obere Unteransicht in dieser Reihenfolge hinzufügen. Kann jede Farbe haben, die der Text haben soll

Ein Beispiel finden Sie hier https://github.com/jhurray/AnimatedLabelExample

jhurray
quelle
0

Hier ist eine SwiftUI-Version:

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

Verwenden Sie es so:

Text("slide to unlock")
    .modifier(Shimmer())
Xavier Lowmiller
quelle