iOS7 画面遷移のカスタマイズ 初級

登場人物

  • UIViewControllerAnimatedTransitioning (アニメーションコントローラ)
  • UIViewControllerContextTransitioning (画面遷移コンテキスト)
  • UIViewControllerTransitioningDelegate (画面遷移デリゲート)

実装

1.どのようなアニメーションを行うかを定義したクラスを作成

UIViewControllerAnimatedTransitioningを継承したクラス

@interface HYFadeAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@end

実装はheaderファイル通り

アニメーション時間を指定

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext;

実際のアニメーションを指定

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{

    // 遷移元と遷移先のViewControllerを取得

    UIViewController* fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
   UIViewController* toVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];


    // 遷移先のViewを追加
    UIView* containerView = [transitionContext containerView];
    [containerView insertSubview:toVC.view belowSubview:fromVC.view];

    // アニメーションを指定

    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                 animations:^{
                     fromVC.view.alpha = 0.0;
                 } completion:^(BOOL finished) {
                     [transitionContext completeTransition:YES];
                     fromVC.view.alpha = 1.0;
                 }];
}

2.遷移先のViewControllerのTransitioningDelegateにUIViewControllerTransitioningDelegateを指定

どの遷移で、どのアニメーションコントローラを利用するか指定する(今回は遷移元に実装)

@interface HYViewController () <UIViewControllerTransitioningDelegate>
@end

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showModalView"]) {
        [[segue destinationViewController] setTransitioningDelegate:self];
    }
}


- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:    (UIViewController *)dismissed
{
    return [[HYFadeAnimationController alloc] init];
}

ステータスバーの表示/非表示の挙動 iOS6

たまにレイアウトがくずれたり、ナビゲーションバーがステータスバーの下に潜り込んだりするため、 いろいろなパターンで動作確認してみる

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

//    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
}
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

//    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
}

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

//    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];

}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];    
//    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];

}
no viewWillAppear viewDidAppear viewWillDisappear viewDidDisappear 呼びもと モーダル
1 × ×
2 × ×
3 ×
4 ×

1

他のViewControllerに迷惑をかけないように、viewDidAppearと、viewWillDisappearで行う

×モーダルビューのレイアウトもstatus barの領域が残ったまま

×モーダルの場合、呼びもとのviewWillAppearではstatus barが隠れた状態のサイズとなる。

2

×モーダルビューはレイアウトがstatus barの領域が残ったまま

×呼び出しもとは全画面レイアウトした後にstatus barが表示されるためヘッダーが刺さる

3

○モーダルビュー側は問題なし

×モーダルの場合、呼びもとのviewWillAppearではstatus barが隠れた状態のサイズとなる。

4

○モーダルビュー側は問題なし

×呼び出しもとは全画面レイアウトした後にstatus barが表示されるためヘッダーが刺さる

考察

viewのライフサイクル中にやるのは良くないのかな。

viewWillAppearで自分の領域は全画面と宣言をし、離れる場合(閉じるボタン謳歌など)はそのタイミングで、 もとに戻してあげるのが正しいのかな。

ただし、もともと全画面状態から遷移だったり、次の画面が全画面かどうかわからないため、ポリシーの問題でもあるきがする。

今回は、モーダルビューだったので、閉じる用のdelegateを呼ぶ箇所で、setStatusBarHidden:NOをしていすると想定通りの挙動となった。

- (void)closeView
{
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
    [self.delegate viewControllerDidClosed:self];
}

- (IBAction)tappedCloseButton:(id)sender
{
    [self closeView];
}

WebDBプレスにはじめて記事をかきました

UIButtonのイメージとテキストを両方表示する

Xcode5+Jenkins+CocoaPodsのビルド

CocoaPods導入メモ

すでにあるプロジェクトでcocoapodsを導入するにあたり、ハマったところがあるのでメモ。

ターゲット名にハイフンが入るとダメ?

テスト用のターゲットを"sample-app-test"みたいな名前にしていると、Podfileにtargetを指定した場合、ハイフンで区切られてエラーとなる

target :sample-app-test, :exclusive => false do
    pod 'GHUnitIOS'
    pod 'OCMock'
end

エラー内容はこんな感じ

### Error

```
Pod::DSLError - Invalid `Podfile` file: undefined local variable or method `app' for Podfile:Pod::Podfile
 #
 #  from xxxxxxxxxxxxx/Podfile:9
 #  -------------------------------------------
 #
 >  target :sample-app-test, :exclusive => false do
 #      pod 'GHUnitIOS'
 #  -------------------------------------------
 #

テスト用ターゲットにも適用

基本的にメインのターゲットをコピペすればOK

Build Settings

  • Framework Search Paths
    • "$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks"
  • Header Search Paths
    • ${PODS_HEADERS_SEARCH_PATHS}
  • User-Defined (値は省略)
    • PODS_BUILD_HEADERS_SEARCH_PATHS
    • PODS_HEADERS_SEARCH_PATHS
    • PODS_PUBLIC_HEADERS_SEARCH_PATHS
    • PODS_ROOT

Build Pheases

run scriptの追加

  • Check Pods Manifest.lock
  • Copy Pods Resources

iOS xibを分けない国際化

xibファイルのLocalize

xibファイルに指定したUILabelやUIButtonなどのタイトルの文字を日本語/英語表示したい場合、 xibファイル自体を分けるやり方もあるが、レイアウト等全く同じなのに文言のためだけに2ファイル作らないといけない。

プログラムで指定

またUILabelやUIButtonをコードと紐付けてプログラムで指定方法もあるがそれだけのためにヘッダーファイルやviewDidLoadに追記するのもめんどくさい

「User Defined Runtime Attributes」+ Category

xibエディター上では指定できないパラメータ等をキー値コーディングにて設定できる。
その指定したパラメータを基にCategoryで拡張したメソッドにてNSLocalizeStringを行う。
#import "UILabel+Localize.h"

@implementation UILabel (Localize)

- (void)setLocalizeKey:(NSString*)key
{
    self.text = NSLocalizedString(key, key);
}

@end

xibエディター


あとはLocalizable.stringsを準備すればOK

ReactNativeでAndroid対応する話

前提 ReactNativeでiOS版のアプリをリリースしていて、Android版をリリースする話 トラブルシューティング Build.VERSION_CODES.Q が存在しないエラー compileSdkVersionを29以上にすると解決 メモリー足りないエラー Execu...