iPhoneエレベーター

ScrollViewをエレベーター風にボタンで動くようにしたiPhoneアプリのサンプルコードを描いてみます。


動かすとこんな感じです

サンプルコード

#import “ViewController.h”

@interface ViewController ()

@property BOOL doorOpen;

@property (weak, nonatomic) UIScrollView *scroll;

@end

@implementation ViewController

– (void)viewDidAppear:(BOOL)animated

{

    [self createFloor];

    [self createElevator];

    [self open];

}

– (void)createFloor

{

    float h = CGRectGetMaxY(self.view.bounds);

    UIScrollView *v = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetMaxX(self.view.bounds), h * 5.0)];

    v.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:v];

    

    for (int i=0; i<5; i++) {

        for (int j=0; j<30; j++) {

            UILabel *l = [[UILabel alloc] init];

            l.text = [NSString stringWithFormat:@”%d F”,i + 1];

            float hue = (arc4random() % 10) * 0.1;

            l.textColor = [UIColor colorWithHue:hue saturation:0.6 brightness:0.6 alpha:1.0];

            l.font = [UIFont fontWithName:@”ChalkboardSE-Bold” size:40];

            [l sizeToFit];

            float x = (arc4random() % 7) * 80;

            float y = h * i + (arc4random() % 6) * 50 + 20;

            l.center = CGPointMake(x, y);

            float angle = (arc4random() * 10) * 0.2 * M_PI;

            l.transform = CGAffineTransformMakeRotation(angle);

            [v addSubview:l];

        }

    }

    self.scroll = v;

}

– (void)createElevator

{

    CAShapeLayer *doorR = [CAShapeLayer layer];

    doorR.name = @”door”;

    doorR.lineWidth = 10;

    doorR.strokeColor = [UIColor lightGrayColor].CGColor;

    doorR.fillColor = [UIColor darkGrayColor].CGColor;

    UIBezierPath *pathR = [UIBezierPath bezierPathWithRect:CGRectMake(CGRectGetMidX(self.view.bounds), 0, CGRectGetMidX(self.view.bounds), CGRectGetMaxY(self.view.bounds))];

    [pathR appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(CGRectGetMidX(self.view.bounds) + 40, 20, 100, 160)]];

    pathR.usesEvenOddFillRule = YES;

    doorR.fillRule = kCAFillRuleEvenOdd;

    doorR.path = pathR.CGPath;

    [self.view.layer addSublayer:doorR];

    

    CAShapeLayer *doorL = [CAShapeLayer layer];

    doorL.name = @”door”;

    doorL.lineWidth = 10;

    doorL.strokeColor = [UIColor lightGrayColor].CGColor;

    doorL.fillColor = [UIColor darkGrayColor].CGColor;

    UIBezierPath *pathL = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, CGRectGetMidX(self.view.bounds), CGRectGetMaxY(self.view.bounds))];

    [pathL appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(100, 20, 100, 160)]];

    doorL.fillRule = kCAFillRuleEvenOdd;

    doorL.path = pathL.CGPath;

    [self.view.layer addSublayer:doorL];

    

    CALayer *barR = [CALayer layer];

    barR.frame = CGRectMake(0, 0, 80, CGRectGetMaxY(self.view.bounds));

    barR.backgroundColor = [UIColor grayColor].CGColor;

    barR.shadowRadius = 3.0;

    barR.shadowOpacity = 0.5;

    [self.view.layer addSublayer:barR];

    

    CALayer *barL = [CALayer layer];

    barL.frame = CGRectMake(CGRectGetMaxX(self.view.bounds)-80, 0, 80, CGRectGetMaxY(self.view.bounds));

    barL.backgroundColor = [UIColor grayColor].CGColor;

    barL.shadowRadius = 3.0;

    barL.shadowOpacity = 0.5;

    [self.view.layer addSublayer:barL];

    

    for (int i=0; i<5; i++) {

        float x = CGRectGetMaxX(self.view.bounds) – 40;

        float y = i * 60 + 50;

        UIButton *floorButton = [UIButton buttonWithType:UIButtonTypeSystem];

        [floorButton setTitle:[@(i+1) stringValue] forState:UIControlStateNormal];

        floorButton.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];

        [floorButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];

        [floorButton setTitleColor:[UIColor orangeColor] forState:UIControlStateSelected];

        [floorButton sizeToFit];

        floorButton.layer.borderColor = [UIColor whiteColor].CGColor;

        floorButton.layer.borderWidth = 2;

        floorButton.center = CGPointMake(x, y);

        [self.view addSubview:floorButton];

        

        [floorButton addTarget:self action:@selector(push:) forControlEvents:UIControlEventTouchUpInside];

    }

}

– (void)open

{

    self.doorOpen = YES;

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@”name == %@”, @”door”];

    NSArray *door = [self.view.layer.sublayers filteredArrayUsingPredicate:predicate];

    float p[] = {CGRectGetMidX(self.view.bounds), – 200};

    for (int i=0; i<2; i++) {

        CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@”position.x”];

        a.fromValue = @([door[i] position].x);

        a.toValue = @(p[i]);

        a.repeatCount = 1;

        a.duration = 2.0;

        

        [door[i] setPosition:CGPointMake(p[i], [door[i] position].y)];

        [door[i] addAnimation:a forKey:nil];

    }

}

– (void)close

{

    self.doorOpen = NO;

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@”name == %@”, @”door”];

    NSArray *door = [self.view.layer.sublayers filteredArrayUsingPredicate:predicate];

    

    float p[] = {0, 0};

    for (int i=0; i<2; i++) {

        CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@”position.x”];

        a.fromValue = @([door[i] position].x);

        a.toValue = @(p[i]);

        a.repeatCount = 1;

        a.duration = 2.0;

        

        [door[i] setPosition:CGPointMake(p[i], [door[i] position].y)];

        [door[i] addAnimation:a forKey:nil];

    }

}

– (void)push:(UIButton*)sender

{

    [self.view.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

        if ([obj isKindOfClass:[UIButton class]]) {

            [(UIButton*)obj setSelected:NO];

        }

    }];

    

    sender.selected = YES;

    float h = ([sender.titleLabel.text intValue] – 1) * CGRectGetMaxY(self.view.bounds);

    if (self.doorOpen) {

        [self close];

        

        [UIView animateWithDuration:3.0 delay:2.2 options:0 animations:^{

            [self.scroll setContentOffset:CGPointMake(0, h) animated:NO];

        } completion:^(BOOL finished) {

            [self open];

        }];

    }

}

@end