[iOS] 動的なレイアウトを超手軽に実装する方法

autolayout

概要

AutoLayoutが超手軽に使える、Masonryをご紹介します!

AutoLayoutで動的レイアウト、しかし…

不特定なサイズの画像や長さのテキストを取り扱う時、どうしても固定サイズのViewレイアウトでは限界があります。
コンテンツのサイズに合わせて、動的にレイアウトを調整する必要があります。
また、単にText ViewやImage Viewのサイズを変えればいいのではなく、周辺のViewも調整が必要なので結構大変です。

そんな動的レイアウトの要件に対しては、AutoLayoutがよく使用されます。
AutoLayoutは、親Viewのサイズ変更があった時に子View同士の間隔やサイズを自動で調節してくれる技術です。
そのレイアウト方法は、Constraint(制約)と呼ばれるものをViewに与える事で定義します。
AutoLayoutを使うと有効な時とその利点は以下の2つです:

  1. Interface Builderを使って、動的なサイズや配置のViewヒエラルキーを組む時
    • 親Viewのサイズ変更に合わせてレイアウトしなおす処理を追加で書く必要が無くなる
    • 例: 不特定サイズの画像を表示するTable view cell内のViewのレイアウト
  2. Interface Builderを使わないでViewを組む時
    • 位置とサイズベースではなく、制約ベースでレイアウトを定義できる
    • 例: 「親Viewに対して上下左右に10pxのマージンを設けた子Viewを配置」というセマンティクスで定義できる

2つめは、異なるスタイルで記述できるという事なんですが、利点となるには苦しい理由があります。
僕の場合はInterface Builderを使わないので、2番が利点にならなければ使う意味はほぼありません。

超めんどくさいAutoLayoutの記述

コードベースでのUI実装において、AutoLayoutが利点となるには苦しいその理由は、AutoLayoutをコードで記述すると超冗長になるという点です。
つまり、全然簡単じゃない!楽じゃない!

「親Viewに対して上下左右に10pxのマージンを設けた子Viewを配置」を実際にコードで記述した場合、以下のようになります。

#!objectivec
UIView *superview = self;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[

    //view1 constraints
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeLeft
                                multiplier:1.0
                                  constant:padding.left],   

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeBottom
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeBottom
                                multiplier:1.0
                                  constant:-padding.bottom],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeRight
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeRight
                                multiplier:1
                                  constant:-padding.right],

 ]];

はい、やってられないですね!

Masonryでお手軽AutoLayout

AutoLayoutは便利だけど、NSLayoutConstraintによる記述は前述の通り冗長で面倒です。
それが原因で、利点を感じられず個人的に今まで全く使ってきませんでした。

しかし、Masonryというライブラリがこの悩みを一気に解決してくれました。
このライブラリは、AutoLayoutの学習コストを最小限に押さえつつ、少ない記述量で使用できるようにしてくれるものです。
これは嬉しい!

前述の例「親Viewに対して上下左右に10pxのマージンを設けた子Viewを配置」をMasonryで記述すると:

#!objectivec
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

とっっっっっても簡単ですね!!
制約ベースでの記述は、HTMLとCSSのような感覚で書けるので、とても重宝しそうです。

CocoaPodsで自動生成されたターゲットにPreprocessor Macroを定義する方法

CocoaPods

どんな時に必要?

CocoaPodsベースのプロジェクトでアプリケーションを開発している時で、特に自前のモジュールもpodでパッケージングしている場合に必要になる事があります。
僕の場合は、pod化した自前モジュールをUnitTestしたい時でした。

ビルド方法によってPreprocessor Macroを切り替えたい

サーバサイドのAPIラッパモジュールを例にとって考えてみたいと思います。
サーバサイドには、ステージング環境や本番環境の切り替えが必要な事がよくあります。
また、その切り替え方法にはPreprocessor Macrosを使用するのが一般的だと思います。

達成したい要件は、以下のようにビルド方法によって環境を切り替える事です:

  • UnitTestの時は必ずステージング環境でコンパイル
  • このモジュールを使用するアプリケーションのワーキングスペースorプロジェクトでは、各ターゲットの設定に従う

Prefix Headerでは上手く出来ない

モジュールをpod化していなければ、普通にUnitTestターゲットのprefix header(*.pch)内に定義すればいいだけです。
しかしpod化している場合、モジュールはCocoaPodsが自動で生成したプロジェクトでコンパイルされるため、テストターゲットで定義したフラグは効果がありません。
さらに、Podsプロジェクトのprefix headerとかプロジェクトのBuild Settingsで設定しても、pod installを実行する毎にリセットされてしまいます。

Podfilepost_installでフラグを指定する

Podfileには、podがインストールされた後のイベントをHookできます。(参考: CocoaPods Guides – Podfile Syntax Reference)
これを使って、モジュールのターゲットのPreprocessor Macrosを変更できます。
Podfileは以下のように書きます:

workspace 'APIWrapper'

platform :ios, '6.0'

xcodeproj 'Tests/APIWrapper Tests'

target :'APIWrapper Tests', :exclusive => true do
   pod 'Kiwi/XCTest'
   pod 'APIWrapper', :path => './'
end

# インストール後に実行される処理を記述
post_install do |installer|

  POD_TARGET_NAME = "Pods-APIWrapper Tests-APIWrapper"

  # 変更したいビルドターゲットを探す
  classy_pods_target = installer.project.targets.find{ |target| target.name == POD_TARGET_NAME }
  unless classy_pods_target
    raise ::Pod::Informative, "Failed to find '" << POD_TARGET_NAME << "' target"
  end

  # ビルド設定を追加
  classy_pods_target.build_configurations.each do |config|
    # Debugターゲットのみ変更
    if config.name == "Debug"
      # Preprocessor Macrosを変更する
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
      # ステージングのフラグを立てる
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] <<  "STAGING"
    end
  end

end

これで、UnitTest用ワーキングスペースで、テスト対象のpodモジュールのPreprocessor Macrosを変更できます!
アプリケーション用のワーキングスペースでは、テスト用の設定は全く気にせずにコーディングできます。

[iOS] チップスの吹き出しを簡単に表示する方法

酔っぱらってるけど更新します!!

アプリに新しいボタンを追加した時とか、初めて利用する人に向けてUIの説明をしたい時ってありますよね。
それも、出来るだけユーザさんの操作の邪魔をせずに。
そういう時に、ボタンの上下左右いずれかの場所にバルーンを表示できたら便利ですよね!
そんなバルーンを手軽に表示できるのが、CMPopTipViewです!動作させると以下のようにいい感じで表示してくれます:

CMPopTipView

CMPopTipView

もちろん、バルーンの見た目のカスタマイズ性も充分にあります。
枠や陰、ボコっと出っ張ったグラデーションなどを調整できます。
使い方も簡単!
以下のようなコードを書くだけ!

// PopTipViewのインスタンス作成
CMPopTipView *navBarLeftButtonPopTipView = [[CMPopTipView alloc] initWithMessage:@"A Message"];
navBarLeftButtonPopTipView.delegate = self;
// ナビゲーションバーのボタンに位置を合わせるように指定して表示
[navBarLeftButtonPopTipView presentPointingAtBarButtonItem:self.navigationItem.leftBarButtonItem animated:YES];

// PopTipViewが非表示になった時に呼ばれるデリゲートメソッド
- (void)popTipViewWasDismissedByUser:(CMPopTipView *)popTipView {
  // クリーンアップコード
}

お試しあれ!

iOSネイティブアプリでA/Bテストする方法

SkyLab

ウェブサービスではよくA/Bテストやっているのを見かけますが、ネイティブのiOSとかAndroidアプリではあまり見かけませんよね。
だって面倒だし!

いやいやちょっと待ってください、iOSならSkyLabが便利ですよ!
まさに、iOSやMacのアプリでA/Bテストを簡単に実現するライブラリです。
使い方は簡単。以下に例を説明しますね!

単純に2パターンでテスト

// Simple A/B Test
[SkyLab abTestWithName:@"Title" A:^{
    self.titleLabel.text = NSLocalizedString(@"Hello, World!", nil);
} B:^{
    self.titleLabel.text = NSLocalizedString(@"Greetings, Planet!", nil);
}];

3パターン以上で各パターンが選ばれる確率を指定する

// Split Test with Weighted Probabilities
[SkyLab splitTestWithName:@"Subtitle" choices:@{
    @"Red" : @(0.15),
    @"Green" : @(0.10),
    @"Blue" : @(0.50),
    @"Purple" : @(0.25)
 } block:^(id choice) {
     self.subtitleLabel.text = NSLocalizedString(@"Please Enjoy This Colorful Message", nil);

     if ([choice isEqualToString:@"Red"]) {
         self.subtitleLabel.textColor = [UIColor redColor];
     } else if ([choice isEqualToString:@"Green"]) {
         self.subtitleLabel.textColor = [UIColor greenColor];
     } else if ([choice isEqualToString:@"Blue"]) {
         self.subtitleLabel.textColor = [UIColor blueColor];
     } else if ([choice isEqualToString:@"Purple"]) {
         self.subtitleLabel.textColor = [UIColor purpleColor];
     }
}];

フラグを確率的に立てる

#!objectivec
// Multivariate Test
[SkyLab multivariateTestWithName:@"Switches" variables:@{
    @"Left" : @(0.5),
    @"Center" : @(0.5),
    @"Right" : @(0.5)
 } block:^(NSSet *activeVariables) {
     self.leftSwitch.on = [activeVariables containsObject:@"Left"];
     self.centerSwitch.on = [activeVariables containsObject:@"Center"];
     self.rightSwitch.on = [activeVariables containsObject:@"Right"];
}];

これで、画面遷移を確率的に変更したりといったテストが簡単にできますね!!

[CocoaPods] コマンド一発でライブラリのサンプルを開く方法

じゃりのツイートで知ったのでメモ。

CocoaPods に、一発でpodのサンプルプロジェクトをダウンロードして開いてくれるサブコマンドが追加されたそうな。
どういう事か説明しますね!!

CocoaPodsって何?

CocoaPods はXCodeプロジェクト向けのモジュール管理ツールです。
RubyでいうgemのObjective-C版みたいなもんですね!
世の中で公開されている多くのモジュールを、手軽にXCodeプロジェクトに追加したり削除したりできます。

コマンド一発でライブラリのサンプルを試せる

気になるライブラリを見つけてちょっと試したいなと思ったら、リポジトリをいちいち$ git cloneしてサンプルプロジェクトを開く必要がありました。
それって結構面倒ですよね。

ここでCocoaPodsの登場です。

もしライブラリがCocoaPodsに登録されていたら、任意のディレクトリで以下のコマンドを実行してみてください。

$ pod try < ライブラリ名>

自動的にリポジトリをcloneして、ヒューリスティックにサンプルプロジェクトを見つけ出して、XCodeで開いてくれます!!

pod-try

リリースされたばかりでちょっと 不具合 もあったけど、安定化も時間の問題ですね!

[iOS7] かっこいいカスタムAlertViewモジュール3選

iOS7向けの多機能なアラート、かっこいいデザインのアラートなどを紹介します!

多機能アラート: LMAlertView

GitHub repository

標準アラートビューと同じ見た目で、アラートの中に地図を埋め込んだりアプリ評価を出したりできます。これはイカス!!いい感じのコメントフォームとか簡単に実装できますね!

プログレス表示に便利: MBProgressHUD

GitHub repository

アラートとは少し違うけど似たようなもんなのでご紹介w 処理の進捗表示といった一時的に表示する用途に向いています。
デザインがかっこいい!!使い方も簡単!!バリエーション豊富!!
個人的にはキャンセルボタンが欲しいかな。




デザイン重視: MLAlertView

GitHub repository

こちらはタイトルバーに色がついててちょっとオシャレなアラート。動きもポップ!


[iOS] 矩形領域にスポットライトをあててUIを手軽に説明できるWSCoachMarksView

注目してほしい箇所以外を暗くして、説明文を表示できるコンポーネント。
初回起動時にざっとアプリのUIの使い方を説明するのに調度いいですね!!

ビデオがうまく表示されない方はこちら

WSCoachMarksView on GitHub