Animieren CAShapeLayer Pfad auf animierte Grenzen ändern

Ich habe eine CAShapeLayer (das ist die Schicht, der eine UIView-Unterklasse, deren path sollte immer aktualisiert, wenn die Ansicht bounds Größe ändert. Für diese habe ich überschrieben, die Ebene setBounds: Methode zum zurücksetzen der Pfad:

@interface CustomShapeLayer : CAShapeLayer
@end

@implementation CustomShapeLayer

- (void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];
    self.path = [[self shapeForBounds:bounds] CGPath];
}

Dies funktioniert gut, bis ich animieren, die Grenzen zu ändern. Ich würde gerne den Pfad animieren, neben anderen animierte Grenzen ändern (in einem UIView animation-block), so dass die Schicht Form, immer nimmt seine Größe.

Da die path Eigenschaft nicht animieren standardmäßig, ich kam mit dieser:

Überschreiben der layer - addAnimation:forKey: Methode. In es, herauszufinden, ob ein bounds-animation wird Hinzugefügt, um die Ebene. Wenn dem so ist, erstellen Sie eine zweite explizite animation für die Animation der Pfad entlang der Grenzen durch das kopieren werden alle Eigenschaften aus den Grenzen der animation, die wird an die Methode übergeben. In code:

- (void)addAnimation:(CAAnimation *)animation forKey:(NSString *)key
{
    [super addAnimation:animation forKey:key];

    if ([animation isKindOfClass:[CABasicAnimation class]]) {
        CABasicAnimation *basicAnimation = (CABasicAnimation *)animation;

        if ([basicAnimation.keyPath isEqualToString:@"bounds.size"]) {
            CABasicAnimation *pathAnimation = [basicAnimation copy];
            pathAnimation.keyPath = @"path";
            //The path property has not been updated to the new value yet
            pathAnimation.fromValue = (id)self.path;
            //Compute the new value for path
            pathAnimation.toValue = (id)[[self shapeForBounds:self.bounds] CGPath];

            [self addAnimation:pathAnimation forKey:@"path"];
        }
    }
}

Ich habe diese Idee aus der Lektüre David Rönnqvist ist View-Schicht Synergie Artikel. (Dieser code ist für iOS 8. Auf iOS 7, es scheint, dass Sie haben zu prüfen, die animation ist keyPath gegen @"bounds" und nicht " @"Grenzen.Größe".)

Den aufrufenden code, löst die Ansicht animiert Grenzen ändern würde wie folgt Aussehen:

[UIView animateWithDuration:1.0 delay:0.0 usingSpringWithDamping:0.3 initialSpringVelocity:0.0 options:0 animations:^{
    self.customShapeView.bounds = newBounds;
} completion:nil];

Fragen

Dieser funktioniert auch meistens, aber ich habe zwei Probleme mit ihm:

  1. Gelegentlich, bekomme ich ein Absturz (EXC_BAD_ACCESS) in CGPathApply() beim auslösen dieser Animationen, während ein vorheriger animation ist noch im Gange. Ich bin mir nicht sicher, ob dies hat nichts zu tun mit meinem speziellen Implementierung. Edit: never mind. Ich vergaß zu konvertieren UIBezierPath zu CGPathRef. Danke @antonioviero!

  2. Bei Verwendung der standard-UIView Frühjahr animation, die animation der anzeigen, die Grenzen und der Ebene Weg ist leicht out of sync. Das heißt, der Pfad-animation führt auch zu einer federnden Weg, aber es folgt nicht die Ansicht, die Grenzen genau.

  3. Generell ist dies der beste Ansatz? Es scheint, dass mit einer Schicht Form, deren Pfad ist abhängig von seiner Größe und Grenzen, das sollte animieren, in-sync mit einer beliebigen Grenzen ändert, ist etwas, das sollte nur work™, aber ich bin eine harte Zeit. Ich meine, es muss einen besseren Weg geben.

Andere Dinge, die ich versucht habe

  • Überschreiben der layer - actionForKey: oder die Ansicht actionForLayer:forKey: um wieder eine eigene animation-Objekt für die path Eigenschaft. Ich denke, das wäre der bevorzugte Weg, aber ich habe nicht einen Weg finden, um an der Transaktion die Eigenschaften, die gesetzt werden sollten, das von der umgebenden animation block. Auch, wenn von innen eine animation mit block [CATransaction animationDuration] etc. immer wieder die default-Werte.

Gibt es einen Weg, um (a) festzustellen, dass Sie derzeit in einem animationsblock, und (b), um die Eigenschaften der animation (Dauer -, Animations-Kurve etc.) festgelegt wurden, werden in diesem block?

Projekt

Hier ist die animation: Das orange Dreieck ist der Pfad der Form-Ebene. Die schwarze Kontur ist die frame-der Ansicht hosting der Formebene.

Animieren CAShapeLayer Pfad auf animierte Grenzen ändern

Haben Sie einen Blick auf das Beispiel-Projekt auf GitHub. (Dieses Projekt ist für iOS 8 und erfordert Xcode 6 zu laufen, sorry.)

Update

Jose Luis Piedrahita wies mich zu dieser Artikel von Nick Lockwood, schlägt die folgende Vorgehensweise vor:

  1. Überschreiben der Ansicht actionForLayer:forKey: oder der Ebene actionForKey: Methode und überprüfen, ob die Schlüssel übergeben, die für diese Methode ist die, die Sie animieren möchten (@"path").

  2. Wenn dem so ist, ruft super auf einer der Ebene "normal" animierbare Eigenschaften (wie @"bounds") wird implizit sagen Sie, wenn Sie in einen animationsblock. Wenn Sie die Ansicht (die Ebene delegieren) gibt ein gültiges Objekt (und nicht nil oder NSNull), wir sind.

  3. Legen Sie die Parameter (Dauer, timing-Funktion, etc.) für die Pfad-animation der animation zurück von [super actionForKey:] - und return-Pfad-animation.

Bedeutet ja Super funktionieren unter iOS 7.x. Aber unter iOS 8, wird das Objekt zurückgegeben, die von actionForLayer:forKey: ist nicht ein standard (und dokumentiert) CA...Animation aber eine Instanz der privaten _UIViewAdditiveAnimationAction Klasse (ein NSObject Unterklasse). Da die Eigenschaften dieser Klasse sind nicht dokumentiert ist, kann ich Sie nicht benutzen einfach einen Pfad erstellen der animation.

_Edit: Oder es könnte funktionieren, nachdem alle. Als Nick erwähnt in seinem Artikel einige Eigenschaften wie backgroundColor noch zurück, ein standard CA...Animation auf iOS 8. Ich werde es versuchen out.`

  • path ist eine animierbare property. Und ich denke nicht wirklich, dass dies der beste Ansatz. Wie Sie selbst sehen können, sieht es Dünn. Ich hoffe, zu einer Lösung zu kommen in ein paar Minuten.
  • Ja, aber es nicht animieren, wenn Sie nur den Pfad von innen eine animation mit block. Sie erstellen eine explizite animation, und fügen Sie es auf die Ebene an. Wo sollte ich diese animation, um den Pfad automatisch animiert wird, wenn es verändert wird in einer animation zu sperren?
  • Ich würde alles tun, manuell mit CADisplayLink – IMO wäre es beseitigt die meisten Probleme.
  • Wenn man sich die animation in slow-mo, so scheint es, dass CAShapeLayer's built-in Pfad interpolation ignoriert einfach jeder Fortschritt, Werte, die größer als 1.0 ist, d.h. es unterstützt nicht "überschießen" - Animationen, die erforderlich wäre, für Feder Effekte. Nur eine Beobachtung, nicht sicher, ob das irgendwo dokumentiert, oder ob es möglich, dies zu umgehen.
  • Das ist meine Beobachtung auch. Wenn log-was passiert, Pfad-animation ist eine korrekt konfigurierte CASpringAnimation aber es verhält sich wie eine basic animation. Wenn Sie die gleiche Technik für eine andere Schicht Eigenschaft (wie "Deckkraft" oder "transformieren"), es tut schwingen, als würde man es erwarten.
  • Für #2 würde ich sagen, dass der Pfad ist nicht elastisch. Es ist nur eine illusion der Grenzen zu springen herum, die macht der Pfad scheint zu springen herum, als gut.
  • Danke, das macht Sinn.
  • Dazu arbeitet das Projekt in Xcode 5 und iOS 7. Sie erhalten den aktuellen animieren von Eigenschaften, die aus der Ebene presentationLayer. @CocoaPriest Auch versucht, mit CADisplayLink, aber es gelegentlich verursacht ein blauer Bildschirm auf einem 64-bit-Gerät.
  • Ich weiß nicht, ob es eine richtige Lösung, aber vielleicht könnten Sie nehmen genau die gleiche Vorgehensweise, wie Sie für den Weg selbst und fügen Sie die Animationen nach dem festlegen des Pfades in die Schranken setter. [super setBounds:bounds]ist, wo die Animationen Hinzugefügt werden (werden Sie implizit oder UIView-Animationen), so können Sie die erforderlichen Werte aus der Ebene "Grenzen" animation, wenn es vorhanden ist. Leider kann ich bestätigen, aus Erfahrung, dass die Pfad-Animationen eingespannt werden, wenn mit federnden timing-Funktionen. (Beachten Sie auch, dass UIView animation-block und CATransaction sind 2 verschiedene Dinge)

InformationsquelleAutor Ole Begemann | 2014-07-24
Schreibe einen Kommentar