It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Android】ActionBarを使ってみる ① (メニューボタン)

Android 3.0 (API Level 11) から導入されたActionBarについて、
少しずつ勉強していきたいと思います。

ActionBarとは

Android 3.0以降で提供されているアクティビティ用のウィジェットです。 ActionBarは、4つの領域に分かれており各領域からアプリへの操作を制御することが可能です。

1.App icon

アプリのテーマアイコンを表示する領域です。 タップにより、画面ナビゲーション階層の制御を行います。

2.View control

ドロップダウンメニューやタブコントロールなどビュー切り替えの機能を提供しています。 検索バーを表示して検索機能を実装することも可能です。

3.Action buttons

アプリ操作のアクションボタンを配置する領域です。 ボタンを長押しすることで、アクションの名前を表示します。

4.Action overflow

3.Action buttonsでは、入りきらないボタンや、使用頻度の低いボタンを配置する領域です。

参考

android:targetSdkVersionAPIレベル11以上を指定している場合、
ActionBarはデフォルトで表示されるようになっています。
※ただし、android:minSdkVersionが11以上でない場合は、
ActionBarクラスへアクセスして、APIを実行することができません

ActionBarを非表示にするには

AndroidManifestのActivityにて、 <android:theme="@android:style/Theme.Holo.NoActionBar"> を指定します。

または、 ソースコードからActionBarクラスインスタンスを呼び出し、hide()を実行することでも、
非表示にすることが可能です。

ActionBar actionbar = getActionBar();
actionbar.hide();

ActionBarにメニューボタンを表示する

Action buttonsAction overflowにメニューボタンを配置してみます。

1.メニューリソースの定義

メニューボタンを定義するには、まずres/menuディレクトリにあるメニューリソースxmlを作成します。

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/item1"
        android:icon="@android:drawable/ic_menu_share"
        android:showAsAction="always"
        android:title="hogehoge"/>
</menu>


属性 説明
id リソースID
icon イコン画
showAsAction メニュー項目をどこへ表示するかを指定する
title メニュータイトル
※iconが指定されている場合は、長押しするとタイトルが表示される

※詳しい属性の情報については、メニューリソースのドキュメントを参照してください。

showAsActionは、各メニューボタンをアクションバーのどこへ配置するかの定義で、 下記5つのオプションを指定可能です。

  • never:
    メニューボタンは、常にAction overflow領域へ表示される。

  • ifRoom:
    表示領域があれば、Action buttons領域へ表示される。
    表示できない場合は、Action overflow領域へ表示される。

  • withText:
    ボタンアイコンと一緒にテキストを表示する

  • collapseActionView:
    action viewを関連付けるて折り畳めるメニューを表示する。
    (action viewの指定がない場合は、標準動作となる)

  • always:
    常にAction buttons領域へ表示される。

2.リソースの適用

作成したリソースxmlは、
ActivityクラスのonCreateOptionsMenu()をオーバーライドして、
inflateすることで画面上に表示されます。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.example_menu, menu);
    return true;
}

3.ボタンイベントのハンドリング

各メニューボタンのイベントは、ActivityクラスのonOptionsItemSelected()をオーバーライドして、
ハンドリングすることができます。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.item1:
            Log.d("", "menu1 tap.");
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

参考サイト

http://www.techdoctranslator.com/android/guide/ui/actionbar http://sakplus.jp/2012/02/23/actionbar/

【Android】動画を再生する

Androidで動画を再生するためには、いくつか方法があるみたいですが、
基本的な方法であるMediaPlayerVideoViewを使った再生方法を試してみました。

MediaPlayerを使って再生する

MediaPlayerで動画を再生する為には、SurfaceViewを使用します。
SurfaceViewは、グラフィックを描画する為のViewで
SurfaceHolder.CallbackというInterfaceを使用してViewの管理を行います。

リファレンス

http://developer.android.com/reference/android/media/MediaPlayer.html

レイアウトの準備

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <SurfaceView android:id="@+id/surfaceView" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />

</RelativeLayout>

実装ソース

public class MainActivity extends Activity implements SurfaceHolder.Callback {
    SurfaceView mSurfaceView;
    SurfaceHolder mSurfaceHolder;
    MediaPlayer mMediaPlayer;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // Windowを透明にする
        getWindow().setFormat(PixelFormat.TRANSPARENT);
        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        
        // SurfaceHolderを取得する
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
    }
    
    @Override
    protected void onDestroy() {
        // メディアプレーヤーを解放する
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
        super.onDestroy();
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        String path = Environment.getExternalStorageDirectory().toString() + "/video.mp4";
        mMediaPlayer =  new MediaPlayer();
        try {
            mMediaPlayer.setDataSource(path);
            // 画面にSurfaceHolderを指定する
            mMediaPlayer.setDisplay(holder);
            mMediaPlayer.prepare();
            mMediaPlayer.setOnPreparedListener(new MediaPlayer. OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mMediaPlayer.start();
                }
            });
            
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // メディアプレーヤーを解放する
        if(mMediaPlayer != null){
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }
}

VideoViewで再生する

VideoViewは、内部でMediaPlayerを使用したウィジェット
MediaPlayerよりも簡易に動画の再生が可能です。

リファレンス

http://developer.android.com/reference/android/widget/VideoView.html

レイアウトの準備

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <VideoView android:id="@+id/videoView"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content" />
</RelativeLayout>
    

ファイルPathから再生

VideoView view = (VideoView) findViewById(R.id.videoView);
videoView.setVideoPath(Environment.getExternalStorageDirectory().toString() + "/video.mp4");
videoView.start();

URLから再生

VideoView view = (VideoView) findViewById(R.id.videoView);
videoView.setVideoURI(Uri.parse("http://sample/sample.mp4"));
videoView.start();

再生コントローラを使用する

OSが準備する再生コントローラを使用するには、MediaControllerクラスを使用します。

videoView.setMediaController(new MediaController(this));

注意点

上記サンプルソースでは、setVideoPath()または、setVideoURI()直後にstart()を実行していますが、
setVideoXXメソッドは、非同期であるため準備ができるまで再生を待つ必要があります。
そのため、リスナーを設定し、準備が完了してから再生を行うのが正しい実装です。

VideoView view = (VideoView) findViewById(R.id.videoView);
videoView.setVideoPath(Environment.getExternalStorageDirectory().toString() + "/video.mp4");
videoView.setOnPreparedListener(new OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        videoView.start();
    }
});

再生フォーマット

VideoViewおよびMediaPlayerの再生フォーマットは下記を参照して下さい。

http://developer.android.com/guide/appendix/media-formats.html

【iOS】画面遷移にUIKit Dynamicsのアニメーションを使ってみる

iOS7から使えるようになったカスタムの画面遷移UIViewControllerTransitioningDelegateと 同じくiOS7から使えるようになった物理エンジンのラッパーUIKit Dynamicsを 組み合わせて画面遷移アニメーションを作ってみたいと思います。

今回は、重力によって地面に落ちて画面遷移するアニメーションを作成します。
落ちるときは、画面下に衝突した時に跳ねるようにしています。
また、表示するときは、天井にぶつかって跳ねる用にしました。

UIKit Dynamicsの準備

まずViewControllerの画面遷移で使用するUIViewControllerAnimatedTransitioningに準拠したクラスを作成します。
今回は、UIKit Dynamicsを使ってアニメーションを行いたいので、UIDynamicBehaviorクラスを継承して作成します。

■ DropTransition.h

@interface INNDropViewBehavior : UIDynamicBehavior
<UIViewControllerAnimatedTransitioning>

@end

■ DropTransition.m

// class extension
@interface DropTransition ()
<UIViewControllerAnimatedTransitioning, UIDynamicAnimatorDelegate>

@property (nonatomic, strong) UIDynamicAnimator *animator;
@property (nonatomic, strong) id <UIViewControllerContextTransitioning> transitionContext;

@end

@implementation DropTransition

#pragma mark UIViewControllerAnimatedTransitioning
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    self.transitionContext = transitionContext;
    
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    UIView *containerView = [transitionContext containerView];
    
    // アニメーション対象のView(=遷移先のView)
    UIView *frontView = nil;
    UIView *backView  = nil;
    CGVector gravityDirection;
    
    if (self.isPresent) { // 表示
        
        frontView        = toVC.view;
        backView         = fromVC.view;
        // 画面の表示と非表示で重力の方向を逆にする
        gravityDirection = CGVectorMake(0, -1.0);
        frontView.frame  = CGRectOffset(frontView.frame, 0, frontView.bounds.size.height);
        
    } else { // 画面閉じる
        
        frontView = fromVC.view;
        backView  = toVC.view;
        gravityDirection = CGVectorMake(0, 1.0);
    }
    
    /* Viewの準備 */
    [containerView addSubview:backView];
    
    // アニメーションを行うViewは、跳ね返りをするために縦方向に2倍の高さを取る
    CGRect frame = [transitionContext initialFrameForViewController:fromVC];
    UIView *canvasView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,
                                                                   frame.size.width,
                                                                   frame.size.height * 2)];
    
    [canvasView addSubview:frontView];
    [containerView addSubview:canvasView];
    
    
    /* UIKitDynamicsの準備 */
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:canvasView];
    self.animator.delegate = self;
    
    // 重力
    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[frontView]];
    [self addChildBehavior:gravityBehavior];
    gravityBehavior.gravityDirection = gravityDirection;
    
    // 衝突
    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[frontView]];
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [self addChildBehavior:collisionBehavior];
    
    // property
    UIDynamicItemBehavior *propertyBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[frontView]];
    propertyBehavior.elasticity             = 0.4; // 弾力
    propertyBehavior.friction               = 1.0; // 摩擦
    [self addChildBehavior:propertyBehavior];

    
    [self.animator addBehavior:self];
    
}

// TODO: 物理計算のアニメーションなので、秒数が正確にとれない..
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0;
}

#pragma mark - UIViewControllerAnimatedTransitioning
- (void)animationEnded:(BOOL)transitionCompleted
{
    // contextの解放
    [self.animator.referenceView removeFromSuperview];
    self.animator = nil;
    self.transitionContext = nil;
}

#pragma mark -
#pragma mark UIDynamicAnimatorDelegate
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator
{
    // アニメーションが終わった時点で通知する
    [self.transitionContext completeTransition:YES];
}

@end

ViewControllerの準備

次に、画面遷移元となるViewControllerにUIViewControllerTransitioningDelegateのデリゲートメソッドを準備します。

■ ViewController.m

// 遷移は、segueを使用
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"moveto"]) {
         // 遷移先のViewControllerのtransitionを独自で行うようにdelegateを指定する
        [segue.destinationViewController setTransitioningDelegate:self];
    }
}

#pragma mark - UIViewControllerAnimatedTransitioning
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    DropTransition *transition = [[DropTransition alloc] init];
    transition = NO;
    
    return behavior;
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    DropTransition *behavior = [[DropTransition alloc] init];
    behavior.isPresent = YES;
    
    return transition;

}

これだけで、モーダルの遷移を独自に実装できます。
面白いアニメーションも簡単に実装できそうなので色々試してみたいです。


画面の用途によってアニメーションを切り分けたい場合は、UIViewControllerAnimatedTransitioningは遷移先のViewControllerに設定するでもいいかもしれません。

【Android】Styleの使い方

スタイルとはなに?

レイアウト属性とその値のセットをいくつか組み合わせて, 1つのIDで使用できるように定義したもです。

何のために使用する?

複数のレイアウト属性を共通で使いまわすために使用します。

例えばテキストの”フォント”、”フォントサイズ”、”フォントカラー”などの スタイルのセットを共通で使いまわすことができます。

どうやって使う?

res/values/の配下にxmlファイルを配置します。

idをソース上で使用する場合は、(R.style.*)として参照します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name=“[id名]”>
    <item name="android:textStyle">italic</item>
    <item name="android:textColor">#ffffff</item>
  </style>
  <style name="[id名]" parent="@style/FontAttr">
    <item name="android:textSize">16sp</item>
  </style>
</resources>

layout.xml上で定義する場合は、 style属性に@style/[id名]で参照できます。

<TextView style="@style/[id名]”
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="style text"
        />

どんなもの(スタイル)が定義できる?

<item>に定義できる属性は、classリファレンスにサポートしているXML属性が記載されています。

親のスタイルを継承した場合

定義したスタイルを継承して新たなスタイルを作成することが可能です。 その場合は、<style>タグにparent属性を定義します。

例:

<style name=“[id名]” parent="@style/[親のid]”>
    <item name="android:textSize">16sp</item>
</style>

【Android】selecterを使ってみる

selecterとは

Androiddrawableリソースの一つで、 状態によって、画像や色を差し替えることができる仕組みです。 (StateListDrawableに分類される)

どうやって定義する?

xmlで作成します。 ファイルの置き場所は、res/drawable配下になります。

selecterで表現できるの主な状態には、下記5パターンが存在します。

1.フォーカス時
2.無効状態かつフォーカス時
3.押下状態
4.無効状態
5.通常状態

(ファイル例)

<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- フォーカス時 -->
  <item
    android:state_focused="true"
    android:state_enabled="true"
    android:state_pressed="false"
    android:drawable="@drawable/focused" />
  <!-- 無効状態 かつ フォーカス時 -->
  <item
    android:state_focused="true"
    android:state_enabled="false"
    android:state_pressed="false"
    android:drawable="@drawable/disabled_and_focused" />
  <!-- 押下状態 -->
  <item
    android:state_focused="true"
    android:state_enabled="true"
    android:state_pressed="true"
    android:drawable="@drawable/pressed" />
  <!-- 無効状態 -->
  <item
    android:state_enabled="false"
    android:drawable="@drawable/disabled" />
  <!-- 通常状態 -->
  <item
    android:drawable="@drawable/normal" />
</selector>

selecterのxmlで指定する<Item>タグは、上から評価されるため。 上記すべての状態において別のdrawableを指定したい場合は、 順番どおり記述する必要があります。

どうやって使用する?

レイアウトxmlで指定します。

(ImageViewの画像を状態ごとに変更したいとき)

<ImageView
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:src="@drawable/sample_selecter” />

(TextViewの文字色を選択状態ごとに変更したいとき)

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    android:textColor="@drawable/sample_selecter"/>

@drawable/[selecterのファイル名]で指定することができます。

その他ハマりどころ

TextViewで背景色や、文字色を状態別で変更したい場合にもselecterを使用しますが、 指定する属性(用途)によって<item>タグの指定方法が異なるようです。

(異なる方法で実装すると実行時エラーが発生する)

android:textColorに指定する場合

<selector xmlns:android="http://schemas.android.com/apk/res/android">  
  <item android:state_selected="true"   
           android:color="#ff3f94be" />  
  <item android:state_selected="false"   
           android:color="#99000000" />  
</selector>  

属性値としてandroid:colorとして定義する。

android:backgroundに指定する場合

<selector xmlns:android="http://schemas.android.com/apk/res/android">  
    <item android:state_selected="true" >  
        <color android:color="#66000000" />  
    </item>  
    <item android:state_selected="false">  
        <color android:color="#33000000" />  
    </item>  
</selector> 

<color>タグとして定義する。


selecterは、drawableの一種なので、 レイアウトxml上でdrawable要素に対して指定しますが、 selecterのxml上でも各状態ごとに表示ファイルを分けたい場合などに、 ビットマップのdrawableを指定することがあります。

drawableの中で更にdrawableを指定するという点が、 まだちょっと慣れないです。。 drawableは、種別が多いため基本的な仕組みを理解していないと、 ソースを読むときに苦労しそうです。

参考

[ソフトウェア技術ドキュメントを勝手に翻訳 7.5.3 Drawable リソース] (http://www.techdoctranslator.com/android/guide/resources/available-resources/drawable-resource)

【Android】独自Viewを作成する

最近、Androidの勉強を始めました。

Androidは、様々な要素をxmlで定義できる点が優れていると思います。 ですが、各xmlの関連とそれらをJavaソースから参照する方法を覚えるまでは、 Javaソースを見ても???となることが多々あります。。

Androidで独自のViewを作成した場合のレイアウトファイルの定義方法や、 独自の属性の作成方法について、混乱しそうなのでメモしておきたいと思います。

独自クラスのjavaファイル定義

public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

Viewのコンストラクタは以下の3つが存在します。

1.public View(Context context) 2.public View(Context context, AttributeSet attrs) 3.public View(Context context, AttributeSet attrs, int defStyle)

レイアウトパラメータを指定した場合、 上記2のattrsを持つコンストラクタが利用されます。

スタイルを指定した場合、 上記3のdefStyleを持つコンストラクタが利用されます。

レイアウトxmlでクラスを使う

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <!-- [パッケージ名] + [クラス名]でタグを定義 -->
    <com.example.customviewsample.CustomView />

</RelativeLayout>

新しい属性の定義

標準のTextViewには、textColortextSizeなどの属性が予め用意されていますが、 独自Viewのなかで新たに属性を定義することができます。

どうやって(どこに)定義する?

属性は、res/values/attrs.xmlを作成し定義します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="CustomView">
   <attr name="custom_title_width" format="integer"/>
  </declare-styleable>
</resources>

<declare-styleable> name属性には、独自Viewのクラス名を定義する。

<attr> name属性には、新たに定義する属性名を定義。 format属性には、属性のフォーマット名を定義する。

formatに指定できる値には下記があります。 ・integer ・float ・boolean ・string ・color ・dimension

定義した値は、どうやって使う?

独自Viewを定義したレイアウトファイルに以下のように記載します。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:myApp="http://schemas.android.com/apk/res/com.example.customviewsample”
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <!-- [パッケージ名] + [クラス名]でタグを定義 -->
    <com.example.customviewsample.CustomView 
        myApp:custom_title_width="123"/>

</RelativeLayout>

1.名前空間を定義する

xmlns:[名前]=“http://schemas.android.com/apk/res/[パッケージ名]“

2.属性に値をセット

myApp:custom_title_width="123"

1で定義した名前からres/values/attrs.xmlで定義した属性を参照することができます。

Javaファイルから読み込み

public class CustomView extends View {

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
        int size = array.getInt(R.styleable.CustomView_custom_title_width, 3);
        a.recycle();
    }

    public CustomView(Context context) {
        super(context);
    }
}

コンストラクタで渡されるAttributeSetからレイアウトファイルで定義した属性値を取得することができます。

context.obtainStyledAttributes()は、引数にAttributeSetint[]をとり、int[]はarrtibuteのid配列を指しています。

これを指定する定数は、R.styleable.[※1独自クラス名]で参照することができます。

TypedArrayクラスのgetInt(int index, int defValue)は、 第一引数にTypedeArrayのindexを渡しますが、ここには、取得したいarrtibuteの属性のindexを指定します。

指定する定数は、R.styleable.[※1独自クラス名]_[※2属性名]で参照することが可能です。

※1 res/values/attrs.xml<declare-styleable>タグのname属性 

※2 res/values/attrs.xml<attr>タグのname属性

UIViewControllerのカスタム画面遷移① (モーダル遷移)

iOS7から、UIViewControllerの画面遷移を自由にカスタムできるようになりました。 今回は、UIViewControllerモーダル遷移について記載します。

遷移元ViewControllerの実装

遷移元のViewControllerにて、 UIViewControllerTransitioningDelegateを継承します。

/*
 アニメーションコントローラを返すメソッド(モーダルビュー表示)
 */
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
                                                                   presentingController:(UIViewController *)presenting
                                                                       sourceController:(UIViewController *)source
{
    // UIViewControllerAnimatedTransitioningを継承したNSObjectを返す ※後述
    return [[TransitionObject alloc] init];
}


/*
 アニメーションコントローラを返すメソッド(モーダルビュー消去)
 */
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    // UIViewControllerAnimatedTransitioningを継承したNSObjectを返す ※後述
    return [[TransitionObject alloc] init];
}

Segueを使用した画面遷移

StroryBordのSegueを利用する場合は、アクション実装箇所で下記のようにSegueの遷移を呼び出します。 (StroryBord上は、ModalStyleのID@“TransitionSegueId”Segueとして定義しておきます)

- (IBAction)pushButton:(id)sender
{
    [self performSegueWithIdentifier:@“TransitionSegueId” sender:nil];
}

prepareForSegueでは、上記で実装したtransitioningDelegateにViewControllerを指定しておきます。

※ modalPresentationStyleは、UIModalPresentationFullScreenまたはUIModalPresentationCustomの必要があるようです。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ViewController *viewController = (INRegisterFeedViewController *)segue.destinationViewController;

    viewController.transitioningDelegate  = self;
    viewController.modalPresentationStyle = UIModalPresentationFullScreen;
}

UIViewControllerAnimatedTransitioningの実装

上記のanimationControllerForPresentedControllerにて指定しているTransitionObjectオブジェクトには、下記2つのメソッドの実装が必要となります。

/**
 *  アニメーションにかかる時間を指定
 *
 */
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5;
}

/**
 *  画面遷移アニメーションを指定
 *
 */
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    // UIViewAnimationなどで任意のアニメーションを指定
}

これだけで簡単にオリジナルのアニメーションを実装できます。 アニメーションモデルを分離して書けるので、シンプルですね。

もっと高度なアニメーション制御もできるようなので、 色々と試してみたいと思います。