98

iOS アプリケーションで通知を表示するための再利用可能なフレームワークを作成しています。UIAlertView のように、通知ビューをアプリケーションの他のすべての上に追加したいと思います。NSNotification イベントをリッスンし、それに応じてビューを追加するマネージャーを初期化するときに、アプリケーションの最上位ビューへの参照を取得する必要があります。これは私が現時点で持っているものです:

_topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

これはどの iOS アプリケーションでも機能しますか、それともトップ ビューを取得するためのより安全で優れた方法ですか?

4

10 に答える 10

113

他のすべてのものの上にオーバーレイを表示したいときはいつでも、アプリケーション ウィンドウの上に直接追加するだけです。

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
于 2010-10-02T00:29:41.393 に答える
47

問題には 2 つの部分があります。トップ ウィンドウ、トップ ウィンドウのトップ ビューです。

既存のすべての回答は、上部のウィンドウ部分を見逃していました。ただし[[UIApplication sharedApplication] keyWindow]、最上位ウィンドウであるとは限りません。

  1. ウインドウ上部。windowLevelアプリで同じウィンドウが 2 つ共存することはほとんどないため、すべてのウィンドウを並べ替えwindowLevelて一番上のウィンドウを取得できます。

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    
  2. トップ ウィンドウのトップ ビュー。完成するだけです。質問ですでに指摘したように:

    UIView *topView = [[topWindow subviews] lastObject];
    
于 2011-11-08T04:03:22.793 に答える
36

通常、これでトップ ビューが表示されますが、ユーザーに表示されるという保証はありません。たとえば、画面からはみ出したり、アルファが 0.0 だったり、サイズが 0x0 だったりする可能性があります。

keyWindow にサブビューがない可能性もあるため、最初にそれをテストする必要があります。これは珍しいことですが、不可能ではありません。

UIWindow は UIView のサブクラスであるため、通知がユーザーに確実に表示されるようにしたい場合は、それを keyWindow に直接追加すると、addSubview:すぐに最上位のビューになります。これがあなたがやろうとしていることかどうかはわかりません。(あなたの質問に基づいて、あなたはすでにこれを知っているようです。)

于 2010-10-01T22:41:23.173 に答える
11

実際には、アプリケーションに複数の UIWindow が存在する可能性があります。たとえば、キーボードが画面上にある場合、[[UIApplication sharedApplication] windows]少なくとも 2 つのウィンドウ (キー ウィンドウとキーボード ウィンドウ) が含まれます。

したがって、ビューを両方の上に表示したい場合は、次のようにする必要があります。

[[[[UIApplication sharedApplication] windows] lastObject] addSubview:view];

(lastObject に windowLevel 優先度が最も高いウィンドウが含まれていると仮定します)。

于 2012-02-28T21:32:01.467 に答える
3

議論ではなく、タイトルが示すように、私は質問に固執しています。任意のポイントで一番上に見えるのはどのビューですか?

@implementation UIView (Extra)

- (UIView *)findTopMostViewForPoint:(CGPoint)point
{
    for(int i = self.subviews.count - 1; i >= 0; i--)
    {
        UIView *subview = [self.subviews objectAtIndex:i];
        if(!subview.hidden && CGRectContainsPoint(subview.frame, point))
        {
            CGPoint pointConverted = [self convertPoint:point toView:subview];
            return [subview findTopMostViewForPoint:pointConverted];
        }
    }

    return self;
}

- (UIWindow *)topmostWindow
{
    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    return topWindow;
}

@end

任意の UIWindow をレシーバーとして、または任意の UIView をレシーバーとして直接使用できます。

于 2013-02-06T18:25:26.943 に答える
1

ローディング ビュー (アクティビティ インジケーター ビューなど) を追加する場合は、UIWindow クラスのオブジェクトがあることを確認してください。ローディング ビューを表示する直前にアクション シートを表示すると、keyWindowは であり、 ではUIActionSheetありませんUIWindow。アクション シートがなくなるので、ローディング ビューも一緒になくなります。または、それが私に問題を引き起こしていたものです。

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
if (![NSStringFromClass([keyWindow class]) isEqualToString:@"UIWindow"]) {
    // find uiwindow in windows
    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        if ([NSStringFromClass([window class]) isEqualToString:@"UIWindow"]) {
            keyWindow = window;
            break;
        }
    }
}
于 2015-11-03T14:55:17.737 に答える
0

これを試して

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
于 2016-08-18T09:02:20.157 に答える