2011-07-06 12 views
18

Piszę aplikację na iPada, która przedstawia dokumenty użytkownika podobne do tych, które prezentuje strona Pages (jako duże ikony rzeczywistego dokumentu). Chcę też naśladować zachowywanie się podczas dotykania przycisku edycji. Jest to ten sam ruchomy wzór, który ikony pojawiają się na ekranie głównym po dotknięciu i przytrzymaniu ich na iPhonie i iPadzie.Algorytm przełączania ikon w systemie iOS

Przeszukałem Internet i znalazłem kilka algorytmów, ale one po prostu powodują, że widok kołysze się w przód iw tył, co wcale nie jest takie, jak w przypadku Apple jiggle. Wydaje się, że jest tam pewna losowość, ponieważ każda ikona drży trochę inaczej.

Czy ktoś ma lub zna jakiś kod, który może ponownie utworzyć ten sam wzór zygzakowaty (lub coś bardzo zbliżonego do niego)? Dzięki!!!

Odpowiedz

12

OK, więc kod openspringboard nie zrobił tego za mnie, ale pozwoliłem sobie na stworzenie kodu, który moim zdaniem jest nieco lepszy, wciąż nie jest doskonały, ale lepszy. Jeśli ktoś ma sugestie, aby to lepiej, chciałbym je usłyszeć ... (dodać do podklasy widoku (y) chcesz skakać)

#define degreesToRadians(x) (M_PI * (x)/180.0) 
#define kAnimationRotateDeg 1.0 
#define kAnimationTranslateX 2.0 
#define kAnimationTranslateY 2.0 

- (void)startJiggling:(NSInteger)count { 

    CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg * (count%2 ? +1 : -1))); 
    CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg * (count%2 ? -1 : +1))); 
    CGAffineTransform moveTransform = CGAffineTransformTranslate(rightWobble, -kAnimationTranslateX, -kAnimationTranslateY); 
    CGAffineTransform conCatTransform = CGAffineTransformConcat(rightWobble, moveTransform); 

    self.transform = leftWobble; // starting point 

    [UIView animateWithDuration:0.1 
          delay:(count * 0.08) 
         options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse 
        animations:^{ self.transform = conCatTransform; } 
        completion:nil]; 
} 

- (void)stopJiggling { 
    [self.layer removeAllAnimations]; 
    self.transform = CGAffineTransformIdentity; // Set it straight 
} 
3

Zapoznaj się z openspringboard project.

W szczególności setIconAnimation: (BOOL) isAnimating w OpenSpringBoard.m. To powinno dać ci kilka pomysłów, jak to zrobić.

32


@ odpowiedź Vic320 jest dobre, ale osobiście nie lubię tłumaczenia. Zmodyfikowałem jego kod, aby zapewnić rozwiązanie, które osobiście uważam, przypomina bardziej efekt wabika trampoliny. Głównie to osiągnąć dodając trochę przypadkowości i skupiając się na rotacji, bez tłumaczenia:

#define degreesToRadians(x) (M_PI * (x)/180.0) 
#define kAnimationRotateDeg 1.0 

- (void)startJiggling { 
    NSInteger randomInt = arc4random_uniform(500); 
    float r = (randomInt/500.0)+0.5; 

    CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians((kAnimationRotateDeg * -1.0) - r)); 
    CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg + r)); 

    self.transform = leftWobble; // starting point 

    [[self layer] setAnchorPoint:CGPointMake(0.5, 0.5)]; 

    [UIView animateWithDuration:0.1 
          delay:0 
         options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse 
         animations:^{ 
           [UIView setAnimationRepeatCount:NSNotFound]; 
           self.transform = rightWobble; } 
         completion:nil]; 
} 
- (void)stopJiggling { 
    [self.layer removeAllAnimations]; 
    self.transform = CGAffineTransformIdentity; 
} 


Credit gdzie kredytowej ze względu jednak @ odpowiedź Vic320 za stanowiły podstawę dla tego kodu, tak +1 do tego.

+0

Jeśli twój punkt kontrolny jest ustawiony gdzie indziej, inny niż 0,5,0.5, obrót spowodowałby, że przycisk "orbitował" wokół punktu kontrolnego. To może być fajny efekt sam w sobie :) –

+0

tak Alex! prawdopodobnie nie jest to pożądane w przypadku OP, ale byłby to fajny efekt. – imnk

+1

Para więcej punktów: [UIView setAnimationRepeatCount: NSNotFound]; nie jest wymagany, ponieważ ustawiono UIViewAnimationOptionRepeat. Aby zatrzymać animację, wykonaj następujące czynności: [self.layer removeAllAnimations]; – sdsykes

3

Dla dobra innych, którzy pojawią się w przyszłości, poczułem, że drgania oferowane przez @ Vic320 były trochę za bardzo zrobotyzowane i porównywanie ich do Keynote było trochę za mocne, a nie organiczne (losowe?). Tak więc w duchu dzielenia się, oto kod, który wbudowałem w moją podklasę UIView ... mój kontroler widoku utrzymuje tablicę tych obiektów, a kiedy użytkownik klika przycisk Edytuj, kontroler widoku wysyła wiadomość startowego do każdego, po czym za pomocą komunikatu stopJiggling, gdy użytkownik naciśnie przycisk Gotowe.

- (void)startJiggling 
{ 
    // jiggling code based off the folks on stackoverflow.com: 
    // http://stackoverflow.com/questions/6604356/ios-icon-jiggle-algorithm 

#define degreesToRadians(x) (M_PI * (x)/180.0) 
#define kAnimationRotateDeg 0.1 
    jiggling = YES; 
    [self wobbleLeft]; 
} 

- (void)wobbleLeft 
{ 
    if (jiggling) { 
     NSInteger randomInt = arc4random()%500; 
     float r = (randomInt/500.0)+0.5; 

     CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians((kAnimationRotateDeg * -1.0) - r)); 
     CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg + r)); 

     self.transform = leftWobble; // starting point 

     [UIView animateWithDuration:0.1 
         delay:0 
        options:UIViewAnimationOptionAllowUserInteraction 
       animations:^{ self.transform = rightWobble; } 
       completion:^(BOOL finished) { [self wobbleRight]; } 
      ]; 
    } 
} 

- (void)wobbleRight 
{ 
    if (jiggling) { 

     NSInteger randomInt = arc4random()%500; 
     float r = (randomInt/500.0)+0.5; 

     CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians((kAnimationRotateDeg * -1.0) - r)); 
     CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg + r)); 

     self.transform = rightWobble; // starting point 

     [UIView animateWithDuration:0.1 
         delay:0 
        options:UIViewAnimationOptionAllowUserInteraction 
       animations:^{ self.transform = leftWobble; } 
       completion:^(BOOL finished) { [self wobbleLeft]; } 
     ]; 
    } 

} 
- (void)stopJiggling 
{ 
    jiggling = NO; 
    [self.layer removeAllAnimations]; 
    [self setTransform:CGAffineTransformIdentity]; 
    [self.layer setAnchorPoint:CGPointMake(0.5, 0.5)]; 
} 
2

Więc jestem pewien, że będę się wrzasnął do pisania kodu brudny (prawdopodobnie istnieją prostsze sposoby na to, że nie jestem świadomy, bo jestem pół-początkujący robią), ale to jest bardziej losowa wersja algorytmu Vic320, która zmienia ilość rotacji i translacji. Decyduje również losowo, w którym kierunku będzie drgać pierwszy, co daje bardziej losowy wygląd, jeśli masz jednocześnie wiele rzeczy, które chyboczą się. Jeśli wydajność jest dla Ciebie dużym problemem, nie używaj go. To właśnie wymyśliłem sposób, w jaki wiem, jak to zrobić.

Dla każdego, kto zastanawia się, czy musisz #import <QuartzCore/QuartzCore.h> i dodać go do połączonych bibliotek.

#define degreesToRadians(x) (M_PI * (x)/180.0) 

- (void)startJiggling:(NSInteger)count { 
    double kAnimationRotateDeg = (double)(arc4random()%5 + 5)/10; 
    double kAnimationTranslateX = (arc4random()%4); 
    double kAnimationTranslateY = (arc4random()%4); 

    CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg * (count%2 ? +1 : -1))); 
    CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians(kAnimationRotateDeg * (count%2 ? -1 : +1))); 
    int leftOrRight = (arc4random()%2); 
    if (leftOrRight == 0){ 
     CGAffineTransform moveTransform = CGAffineTransformTranslate(rightWobble, -kAnimationTranslateX, -kAnimationTranslateY); 
     CGAffineTransform conCatTransform = CGAffineTransformConcat(rightWobble, moveTransform); 
     self.transform = leftWobble; // starting point 

     [UIView animateWithDuration:0.1 
           delay:(count * 0.08) 
          options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse 
         animations:^{ self.transform = conCatTransform; } 
         completion:nil]; 
    } else if (leftOrRight == 1) { 
     CGAffineTransform moveTransform = CGAffineTransformTranslate(leftWobble, -kAnimationTranslateX, -kAnimationTranslateY); 
     CGAffineTransform conCatTransform = CGAffineTransformConcat(leftWobble, moveTransform); 
     self.transform = rightWobble; // starting point 

     [UIView animateWithDuration:0.1 
           delay:(count * 0.08) 
          options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse 
         animations:^{ self.transform = conCatTransform; } 
         completion:nil]; 
    } 
} 

- (void)stopJiggling { 
    [self.layer removeAllAnimations]; 
    self.transform = CGAffineTransformIdentity; // Set it straight 
} 
+0

Podoba mi się twój wybór, aby losowo wybrać kierunek, w którym zaczniesz poruszać. Zamierzam zaktualizować mój kod podobnie !! – tobinjim

6

Oto jak animowałem moją podklasę CALayer - zainspirowaną innymi odpowiedziami - używając wyraźnej animacji.

-(void)stopJiggle 
{ 
    [self removeAnimationForKey:@"jiggle"]; 
} 

-(void)startJiggle 
{ 
    const float amplitude = 1.0f; // degrees 
    float r = (rand()/(float)RAND_MAX) - 0.5f; 
    float angleInDegrees = amplitude * (1.0f + r * 0.1f); 
    float animationRotate = angleInDegrees/180. * M_PI; // Convert to radians 

    NSTimeInterval duration = 0.1; 
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; 
    animation.duration = duration; 
    animation.additive = YES; 
    animation.autoreverses = YES; 
    animation.repeatCount = FLT_MAX; 
    animation.fromValue = @(-animationRotate); 
    animation.toValue = @(animationRotate); 
    animation.timeOffset = (rand()/(float)RAND_MAX) * duration; 
    [self addAnimation:animation forKey:@"jiggle"]; 
} 
2

ja inżynierii odwrotnej Jabłko Stringboard i zmodyfikowane nieco ich animację, a poniżej kod jest naprawdę dobre rzeczy.

+ (CAAnimationGroup *)jiggleAnimation { 
CAKeyframeAnimation *position = [CAKeyframeAnimation animation]; 
position.keyPath = @"position"; 
position.values = @[ 
        [NSValue valueWithCGPoint:CGPointZero], 
        [NSValue valueWithCGPoint:CGPointMake(-1, 0)], 
        [NSValue valueWithCGPoint:CGPointMake(1, 0)], 
        [NSValue valueWithCGPoint:CGPointMake(-1, 1)], 
        [NSValue valueWithCGPoint:CGPointMake(1, -1)], 
        [NSValue valueWithCGPoint:CGPointZero] 
        ]; 
position.timingFunctions = @[ 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] 
          ]; 
position.additive = YES; 

CAKeyframeAnimation *rotation = [CAKeyframeAnimation animation]; 
rotation.keyPath = @"transform.rotation"; 
rotation.values = @[ 
        @0, 
        @0.03, 
        @0, 
        [NSNumber numberWithFloat:-0.02] 
        ]; 
rotation.timingFunctions = @[ 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] 
          ]; 

CAAnimationGroup *group = [[CAAnimationGroup alloc] init]; 
group.animations = @[ position, rotation ]; 
group.duration = 0.3; 
group.repeatCount = HUGE_VALF; 
group.beginTime = arc4random() % 30/100.f; 
return group; 
} 

oryginalny animacja jabłko skakać:

CAKeyframeAnimation *position = [CAKeyframeAnimation animation]; 
position.beginTime = 0.8; 
position.duration = 0.25; 
position.values = @[[NSValue valueWithCGPoint:CGPointMake(-1, -1)], 
        [NSValue valueWithCGPoint:CGPointMake(0, 0)], 
        [NSValue valueWithCGPoint:CGPointMake(-1, 0)], 
        [NSValue valueWithCGPoint:CGPointMake(0, -1)], 
        [NSValue valueWithCGPoint:CGPointMake(-1, -1)]]; 
position.calculationMode = @"linear"; 
position.removedOnCompletion = NO; 
position.repeatCount = CGFLOAT_MAX; 
position.beginTime = arc4random() % 25/100.f; 
position.additive = YES; 
position.keyPath = @"position"; 

CAKeyframeAnimation *transform = [CAKeyframeAnimation animation]; 
transform.beginTime = 2.6; 
transform.duration = 0.25; 
transform.valueFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ]; 
transform.values = @[@(-0.03525565),@(0.03525565),@(-0.03525565)]; 
transform.calculationMode = @"linear"; 
transform.removedOnCompletion = NO; 
transform.repeatCount = CGFLOAT_MAX; 
transform.additive = YES; 
transform.beginTime = arc4random() % 25/100.f; 
transform.keyPath = @"transform"; 

[self.dupa.layer addAnimation:position forKey:nil]; 
[self.dupa.layer addAnimation:transform forKey:nil]; 
+0

O mój Boże, kocham Cię, to jest idealne – Jota

2

@mientus oryginalnym kodzie jabłko szarpania Swift 4, z opcjonalnymi parametrów dostosować czas trwania (np prędkości), przemieszczenie (to znaczy zmiany położenia) i o (tj. kwota rotacji).

private func degreesToRadians(_ x: CGFloat) -> CGFloat { 
    return .pi * x/180.0 
} 

func startWiggle(
    duration: Double = 0.25, 
    displacement: CGFloat = 1.0, 
    degreesRotation: CGFloat = 2.0 
    ) { 
    let negativeDisplacement = -1.0 * displacement 
    let position = CAKeyframeAnimation.init(keyPath: "position") 
    position.beginTime = 0.8 
    position.duration = duration 
    position.values = [ 
     NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)), 
     NSValue(cgPoint: CGPoint(x: 0, y: 0)), 
     NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: 0)), 
     NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)), 
     NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)) 
    ] 
    position.calculationMode = "linear" 
    position.isRemovedOnCompletion = false 
    position.repeatCount = Float.greatestFiniteMagnitude 
    position.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25))/Float(100)) 
    position.isAdditive = true 

    let transform = CAKeyframeAnimation.init(keyPath: "transform") 
    transform.beginTime = 2.6 
    transform.duration = duration 
    transform.valueFunction = CAValueFunction(name: kCAValueFunctionRotateZ) 
    transform.values = [ 
     degreesToRadians(-1.0 * degreesRotation), 
     degreesToRadians(degreesRotation), 
     degreesToRadians(-1.0 * degreesRotation) 
    ] 
    transform.calculationMode = "linear" 
    transform.isRemovedOnCompletion = false 
    transform.repeatCount = Float.greatestFiniteMagnitude 
    transform.isAdditive = true 
    transform.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25))/Float(100)) 

    self.layer.add(position, forKey: nil) 
    self.layer.add(transform, forKey: nil) 
} 
Powiązane problemy