UIViewAnimationOptionsの複数指定 - 同時に適用できるものとできないものについて

## はじめに

UIViewAnimationOptionsですが、複数のオプションを指定したときに予想外の動きをするときがあります。
例えば、TransitionCurlUpとTransitionCurlDownを指定すると、TransitionFlipFromBottomと同じ動きになります。

```swift:example
var opt = UIViewAnimationOptions.TransitionCurlUp | UIViewAnimationOptions.TransitionCurlDown
UIView.transitionWithView(view, duration: 1.0, options: opt, animations: {
    // some code
    // ここのアニメーションはTransitionFlipFromBottomの動きになる
}, completion: { finished in
    // some code
})
```
そこでなぜこうなるのかをUIViewAnimationOptionsの定義を調べまとめました。

([こちら](http://rayc5.hatenablog.com/entry/2015/06/20/074031)にまとめたものの簡略版です)

## UIViewAnimationOptionsについて

[公式ドキュメント](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/c/tdef/UIViewAnimationOptions)で詳細を調べました。

そこでは、UIViewAnimationOptionsはObjective-Cで以下のように実装されていることが書いてあります。

```objc:UIViewAnimationOptions
enum {
   UIViewAnimationOptionLayoutSubviews            = 1 <<  0,
   UIViewAnimationOptionAllowUserInteraction      = 1 <<  1,
   UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2,
   UIViewAnimationOptionRepeat                    = 1 <<  3,
   UIViewAnimationOptionAutoreverse               = 1 <<  4,
   UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5,
   UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6,
   UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7,
   UIViewAnimationOptionShowHideTransitionViews   = 1 <<  8,
   UIViewAnimationOptionOverrideInheritedOptions  = 1 <<  9,
   
   UIViewAnimationOptionCurveEaseInOut            = 0 << 16,
   UIViewAnimationOptionCurveEaseIn               = 1 << 16,
   UIViewAnimationOptionCurveEaseOut              = 2 << 16,
   UIViewAnimationOptionCurveLinear               = 3 << 16,
   
   UIViewAnimationOptionTransitionNone            = 0 << 20,
   UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,
   UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,
   UIViewAnimationOptionTransitionCurlUp          = 3 << 20,
   UIViewAnimationOptionTransitionCurlDown        = 4 << 20,
   UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,
   UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,
   UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
};
typedef NSUInteger UIViewAnimationOptions;
```

複数指定の際に " | " を使うあたりから予想できる通り、ビット演算を利用して様々なオプションが管理されていますね。

UIViewAnimationOptionTransition~に関するものだけに焦点を当てて考えます。
これらのオプションは0~7までの数字を20ビット分左シフトしています。シフトのことは一旦おいておいて、0~7までの数字を2進数表示すると、以下のようになります。

|オプション名|10進数|2進数|
|------------|-----|--------|
|TransitionNone|0|000|
|TransitionFlipFromLeft|1|001|
|TransitionFlipFromRight |2|010|
|TransitionCurlUp|3|011|
|TransitionCurlDown |4|100|
|TransitionCrossDissolve|5|101|
|TransitionFlipFromTop |6|110|
|TransitionFlipFromBottom|7|111|

これを見ると複数指定するとおかしい動きをする理由がわかります。

TransitionCurlUp | TransitionCurlDown は 011 | 100 = 111 なので TransitionFlipFromBottomになります。

<b>この実装からわかるように基本的にTransition系のオプションを2つ以上指定することは推奨されていません。</b>オプションの複数指定は、RepeatとTransition系のオプションを指定するという様に、違う種類のオプションを指定するときに使う必要があります。

## 複数指定して良いオプションについて

UIViewAnimationOptionsの詳細を理解したため、どのオプションが同時に指定できるのかが定義からわかります。

まず最初に書かれている10個のオプション(UIViewAnimationOptionLayoutSubviews ~ UIViewAnimationOptionOverrideInheritedOptions)は、2進数の1桁目から10桁目までのそれぞれの桁がそれぞれのオプションのフラグになっているので、それぞれ独立していることがわかります。したがって、複数のオプションを指定しても大丈夫です。

次に書かれているUIViewAnimationOptionCurveEaseInOut~UIViewAnimationOptionCurveLinearのオプションはTransition系のときと同様に複数指定すると想定外の動作をします。
例えば、EaseInとEaseOutを指定するとLinearになります。

(これに関してはLinearとEaseInOutを交換すると、EaseIn | EaseOut = EaseInOutになり、もし複数指定しても文字通りに動作するのでそうしたら良いのになと思います。)