2

I am trying to implement a animation in objective-c where I have a NY skyline illustration as attached image. Requirement is to implement city lights animations where lights (square and rectangle boxes) in skyline buildings lit sequentially... in an appealing animated way. One of the ways to achieve this is using a set of sequential images (UIImageView.animationImages = array of images) but I am sure there might be better and more efficient ways to achieve this. I would greatly appreciate if someone can point me in the right direction.

enter image description here

Alex
  • 833
  • 3
  • 15
  • 28

3 Answers3

2
[UIView animateWithDuration:1 delay:0 options:0 animations:^{
    light1.alpha = 1;
} completion:nil];

[UIView animateWithDuration:1 delay:0.5 options:0 animations:^{
    light2.alpha = 1;
} completion:nil];

[UIView animateWithDuration:1 delay:1 options:0 animations:^{
    light3.alpha = 1;
} completion:nil];

[UIView animateWithDuration:1 delay:1.5 options:0 animations:^{
    light4.alpha = 1;
} completion:nil];

Set the light views alpha to 0 when you create them, and run this in -viewDidAppear maybe... (This is assuming each light is a separate UIView...)

If you have a lot of views you could do this to animate each one's alpha value:

float n = 0; 
for (UIView* v in self.view.subviews) { 
    if (v.alpha < 0.5) { 
        [UIView animateWithDuration:1 delay:n options:0 animations:^{ 
            v.alpha = 1; 
        } completion:nil]; 
        n += 0.5;
    } 
}
jjv360
  • 4,120
  • 3
  • 23
  • 37
  • Thanks for your answer. There are lot of lights in total (combining all buildings), for example - in 2nd building from left I have 15 rows of lights. You think I should have a UIView for each row of lights in each building? – Alex Sep 05 '12 at 15:18
  • That is a lot of views, it may cause some slowness... But if you want to try that you can simplify the animation code by iterating over subviews: `float n = 0; for (UIView* v in self.view.subviews) { if (v.alpha < 0.5) { [UIView animateWithDuration:1 delay:n options:0 animations:^{ v.alpha = 1; completion:nil]; n += 0.5 } }` – jjv360 Sep 05 '12 at 15:23
  • Thanks. To summarize the approach, for the 2nd building from left (with 15 rows of lights) I should first programatically add 60 (15 X 4) UIViews with alpha = 0 and than turn on their alpha to 1 in a loop. Do the same for all other buildings? Am I understanding correctly? – Alex Sep 05 '12 at 16:22
1

Why not have an array of CGRect's with the coords of each light. Then you can simply move 1 UIView around the rects, or if lighting more than 1 at a time then move a couple of UIViews around.

EDIT------- Just some quick code thrown together.

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blackColor];
    rects = [[NSMutableArray alloc] init];
    lit = [[NSMutableArray alloc] init];

    [rects addObject:[NSValue valueWithCGRect:CGRectMake(20, 20, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(60, 20, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(100, 20, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(140, 20, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(180, 20, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(220, 20, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(260, 20, 20, 20)]];

    [rects addObject:[NSValue valueWithCGRect:CGRectMake(20, 60, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(60, 60, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(100, 60, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(140, 60, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(180, 60, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(220, 60, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(260, 60, 20, 20)]];

    [rects addObject:[NSValue valueWithCGRect:CGRectMake(20, 100, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(60, 100, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(100, 100, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(140, 100, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(180, 100, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(220, 100, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(260, 100, 20, 20)]];

    [rects addObject:[NSValue valueWithCGRect:CGRectMake(20, 140, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(60, 140, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(100, 140, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(140, 140, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(180, 140, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(220, 140, 20, 20)]];
    [rects addObject:[NSValue valueWithCGRect:CGRectMake(260, 140, 20, 20)]];
}

- (void)viewDidAppear:(BOOL)animated
{
    [self lightRandomLight];
    [self performSelector:@selector(switchOffRandomLight) withObject:nil afterDelay:1.0];
}

- (void)lightRandomLight
{
    BOOL escape = NO;
    int rand;

    while (!escape) {
        BOOL alreadyLit = NO;
        rand = arc4random() % [rects count];
        // Check if already lit
        for (UIView *view in lit) {
            CGRect litRect = view.frame;
            CGRect ranRect = [[rects objectAtIndex:rand] CGRectValue];
            if (CGRectContainsRect(litRect, ranRect)) {
                alreadyLit = YES;
            }
        }

        if (!alreadyLit) {
            UIView *light = [[UIView alloc] initWithFrame:[[rects objectAtIndex:rand] CGRectValue]];
            light.backgroundColor = [UIColor yellowColor];
            [lit addObject:light];
            [self.view addSubview:light];
            escape = YES;
        }
    }

    [self performSelector:@selector(lightRandomLight) withObject:nil afterDelay:0.2];
}

- (void)switchOffRandomLight
{
    int rand = arc4random() % [lit count];
    UIView *light = [lit objectAtIndex:rand];
    [lit removeObject:light];
    [light removeFromSuperview];

    [self performSelector:@selector(switchOffRandomLight) withObject:nil afterDelay:0.5];
}
Darren
  • 10,182
  • 20
  • 95
  • 162
  • Thanks Darren. That sounds like a good approach. Initially all lights would be OFF than they would start to lit sequentially but at some point all lights of all buildings would be lit. Would this approach still work in such scenario? Would you be able to provide a pseudo code? I would greatly appreciate that. – Alex Sep 05 '12 at 15:41
  • Thanks Darren. I will try this approach and will let you know how it goes. Thank you for taking out time and putting a pseudo code for my understanding. – Alex Sep 05 '12 at 16:33
  • I know you might not use this, but I was bored and quite liked this task. I've edited my code above. Put it in a ViewController and watch all the lights flicker on and off until they are all on. – Darren Sep 05 '12 at 17:47
  • Darren, I tested this solution and it works wonderfully. Just wondering if I want to lit the lights sequentially (row 1 than row 2 than row 3 etc), is it possible to assign some kind of tag and lit them by tags? – Alex Sep 06 '12 at 10:19
  • You could put the CGrects into the array in the order you want them lit, then instead of usin the random number, just iterate over the array from first to last. – Darren Sep 06 '12 at 10:49
  • Darren, I have lot of other subviews (UIImageviews) on my page and while this animation is running, I am able to access all subviews BUT once the animation is complete (all lights are ON) I cannot access any subviews. Even swipe and other gestures stop working after the animation is complete. What could be the issue? Any pointers would be greatly helpful. – Alex Sep 13 '12 at 17:55
  • I'd guess that your lights views are on top of the views you want to interact with? You'd need to bring them to the front. – Darren Sep 13 '12 at 17:59
  • That's what I had thought and called bringSubviewToFront for views I wanted to access but it didn't help. Also the frame size of the light views is so negligible that it shouldn't block other views - correct? Not sure what could be the issue. Any other suggestions? – Alex Sep 13 '12 at 18:35
  • Your probably better starting another question and posting some code. – Darren Sep 13 '12 at 19:09
  • Hi Darren, As per your suggestion, I have posted a new question here with my code: http://stackoverflow.com/questions/12478685/all-uielements-become-inaccessible-after-animation-completes. Would greatly appreciate if you can review and provide your feedback. – Alex Sep 18 '12 at 14:23
  • I have posted code to fix the problem for you, in your new question. – Darren Sep 18 '12 at 17:30
0

This might be a bit of work, but I might try to set the window color to alpha, and then "flip on" the lights behind the image.

Mike M
  • 4,358
  • 1
  • 28
  • 48