yaakaito.org

Storyboardで横にずれるメニュー作る

Objective-C, Storyboard, iOS

こんにちは!うきょーです! Storyboardって、FaceBookとかがやってる横にずれるメニューを作るのがめちゃくちゃめんどくさそう、というかそこだけコードで書くんじゃないの?という印象だったんですが、 ESSlidingViewControllerというのがStroyboardに対応しているらしく、綺麗にいけそうなので試してみた。

コードはGithubで

プロジェクトつくってセットアップ

とりあえずSingle View Applicationを選んで適当に

作ったらCocoapodsで

1
2
3
platform :ios

pod 'ECSlidingViewController'
1
$ pod install

ECSlidingViewControllerを設定する

Storyboardに最初に出てくるViewControllerのクラスをECSlidingViewControllerにする。

MenuViewControllerみたいなのをUITableViewControllerで作って、Storyboardで関連付ける。 Menuというidentifierを与えておく。

これでAppDelegateをいじるのかなーと思ったんだけど、ついてくるサンプルを読んだら、 InitialSlidingViewControllerみたいなを作って、これで初期化してた、こっちの方が綺麗な感じがするのでこっちでやることにする。 最初に表示されるViewControllerを設定しているので、それを作つくるんだけど、ついでなので2つ追加して全体でこんなStoryboardにした。 それぞれにFirstViewとSecondViewでidentifiterを与えている。

topViewControllerというのが生えているので、ここへFirstViewControllerみたいなのを入れる。

1
2
3
4
5
6
7
8
9
10
11
@implementation SSInitialViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    self.topViewController = [storyboard instantiateViewControllerWithIdentifier:@"FirstView"];
}

@end

ここまでやったらFirstViewControllerの方でメニューなんかを追加する。 グローバルな感じで設定するんじゃなくて、ViewController単位に指定できるっぽい、便利。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@implementation SSFirstViewController

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.view.layer.shadowOpacity = 0.75f;
    self.view.layer.shadowRadius = 4.0f;
    self.view.layer.shadowColor = [UIColor blackColor].CGColor;

    if (![self.slidingViewController.underLeftViewController isKindOfClass:[SSMenuViewController class]]) {
        UIViewController *menu =  [self.storyboard instantiateViewControllerWithIdentifier:@"Menu"];
        self.slidingViewController.underLeftViewController = menu;
    }

    [self.view addGestureRecognizer:self.slidingViewController.panGesture];
    [self.slidingViewController setAnchorRightRevealAmount:280.0f];
}

@end

ところでここで一回ハマってて、[self.slidingViewController setAnchorRightRevealAmount:280.0f]はデフォルトとかがあるわけじゃなくて、かならず設定しないと駄目っぽい。

こんなかんじ!

メニューの実装してみる

適当に、面倒なのでメニュー名からidentifier作るようにしてる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@interface SSMenuViewController ()
@property (strong, nonatomic) NSArray *items;
@end

@implementation SSMenuViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.items = @[@"First", @"Second"];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"MenuCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    cell.textLabel.text = self.items[indexPath.row];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *identifier = [NSString stringWithFormat:@"%@View", self.items[indexPath.row]];
    UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];

    [self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
        CGRect frame = self.slidingViewController.topViewController.view.frame;
        self.slidingViewController.topViewController = newTopViewController;
        self.slidingViewController.topViewController.view.frame = frame;
        [self.slidingViewController resetTopView];
    }];
}

@end

画面の切り替えはtableView:didSelectRowAtIndexPath:でやってるあたり。

1
2
3
4
5
6
7
8
9
10
11
12
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *identifier = [NSString stringWithFormat:@"%@View", self.items[indexPath.row]];
    UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];

    [self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
        CGRect frame = self.slidingViewController.topViewController.view.frame;
        self.slidingViewController.topViewController = newTopViewController;
        self.slidingViewController.topViewController.view.frame = frame;
        [self.slidingViewController resetTopView];
    }];
}

storyboardからidentifierで引いてきて、それをtopViewConttollerに設定するだけの親切構造。 僕がいままで使ってきたライブラリだと、この辺は自由にやってね、が多かったので、こうするとよいよがあると助かる。

ほとんどサンプルのまま持ってきたんだけど、このままだとメニューをタップしたあとのアニメーションがちょっとうざい。(一番右まで行ってから戻るやつ) これはanchorTopViewOffScreenTo:animations:onComplete:がそういうアニメーションっぽいので、resetTopViewだけにする。こっちは元の位置に戻すやつ。

1
2
3
4
5
6
7
8
9
10
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *identifier = [NSString stringWithFormat:@"%@View", self.items[indexPath.row]];
    UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];

    CGRect frame = self.slidingViewController.topViewController.view.frame;
    self.slidingViewController.topViewController = newTopViewController;
    self.slidingViewController.topViewController.view.frame = frame;
    [self.slidingViewController resetTopView];
}

あとはStoryboardでよしなにビュー作ったりしていけばよい、Storyboardすごいなー。

まとめ

思ってたより綺麗にできたし、Storyboard毛嫌いなおりそうでよかったです。