18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UIWindowLevelの怪

Last updated at Posted at 2014-09-01

自前でViewControllerをモーダル表示させたい

iOSでモーダルでViewControllerを表示したい場合、

- (void)presentViewController:animated:completion:

を使うと思うのですが、時に、これを使わずにモーダル表示をさせたい場面があり、その対策の一つとして、専用のUIWindowクラスを作る手があります。

上記のメソッドに似た形で試しに作ってみたのがこちら。
任意のViewControllerを渡すと、それをモーダルっぽく表示してくれます。
ちなみにanimated=YESだと、ふわっとした感じで出てきます。

UIViewController+Alert.m

/**
 @brief 与えられたViewControllerをアラートとして表示する
 */
- (void)presentAlertController:(UIViewController *)viewControllerToPresent
                      animated:(BOOL)animated
                    completion:(void (^)(void))completion {
    
    dispatch_async(dispatch_get_main_queue(), ^{
        UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        window.windowLevel = UIWindowLevelAlert;
        window.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2];
        window.rootViewController = viewControllerToPresent;
        [window makeKeyAndVisible];
        
        self.indicatorWindow = window;
        
        if (animated) {
            window.alpha = 0.1;
            
            [UIView transitionWithView:window duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseInOut
                            animations:^{
                                window.alpha = 1;
                            } completion:^(BOOL finished) {
                                if (completion) {
                                    completion();
                                }
                            }];
        }
    });
}

UIWindowLevel?

さて、これだけであれば平和に解決なのですが、UIWindowにはwindowLevelというプロパティがあります。
これ、要するに「そのWindowがどれぐらい手前に出てくるか」というレベルです。
ちなみにリファレンスを見ると、こうなっておる。

UIWindowLevel.h
const UIWindowLevel UIWindowLevelNormal;
const UIWindowLevel UIWindowLevelAlert;
const UIWindowLevel UIWindowLevelStatusBar;
typedef CGFloat UIWindowLevel;

簡単に言えば、
「通常のwindowよりも、アラートの方が常に手前に出る」
そして更に
「アラートよりも、ステータスバーの方が常に手前に出る」
ということです。それを決定しているのがこのUIWindowLevelなわけです。

通常Windowとアラートの間のレベルが欲しい!!

そうなんです。
実は、そもそも私が自前でモーダル表示のカテゴリメソッドを生やしたのは、
「通常のWindowよりは手前だけど、アラートよりは奥に出るWindowが欲しい」
という要望からでした。

しかし、windowLevelプロパティに設定できるのは上記の3種類だけ・・・?

いやいや、よ〜く見ると

UIWindowLevel.h
typedef CGFloat UIWindowLevel;

と書いてあるじゃないですか。はい。こいつ、単なるCGFloatなんです。

というわけで、そしたら、

UIViewController+Alert.m
window.windowLevel = UIWindowLevelAlert;

の部分を、

UIViewController+Alert.m
window.windowLevel = UIWindowLevelAlert - 1;

に書き換えれば、アラートより奥にwindowが表示されるのでは??

あかんやん

しかし、それではうまくいきませんでした。
もう少し正確に言うと、このUIViewController上から更に普通のUIAlertViewを表示させようとしても、
全く何も起きなくなってしまいました。

なんであかんのかぜんぜんわからへん

ということで、色々とデバッグしてみたところ、実は

UIViewController+Alert.m
window.windowLevel = UIWindowLevelAlert - 4;

だとちゃんと動いたのです!!

-1と-4。
どういうことなのか。
ともかく、

iOSが「WindowLevelに差がある」と判断するには、最低でもWindowLevelの差が4以上は必要

という結論に現在至っております。
しかし理由は全くわかりません。
誰かご存知の方、ご教授いただければと思います。

18
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?