2

これは絶対に紛らわしいです。3.5 インチの iPhone シミュレーターでは、アプリのすべての UIButton が問題なく動作します。ただし、4 インチの iPhone シミュレーターを起動すると、アプリの左側にあるすべての UIButton がクリック イベントを受け取りません。

以下は、3.5 インチ サイズと 4 インチ サイズのスクリーンショットです。4 インチ サイズでは、行を追加しました。その行の左側では、どのボタンもクリック イベントを受け取りません。その行の右側では、すべてのボタンが正常に動作します。ボタン 2、5、および 8 の左側はクリック イベントを受け取ります。クリックには反応しませんが、これらのボタンの右側は反応します。

ここに画像の説明を入力

ここに画像の説明を入力

更新---- @iccirのおかげで、より多くの情報を発見しました。どうやら、私の UIWindow は 568x320 ではなく 320x480 しかありません。UIWindow をキーにして可視にする以外は、コード内で UIWindow に触れていません。MainWindow.xib で、IBOutlet を rootViewController に接続します。

<UIWindow: 0xc097d50; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0xc098460>; layer = <UIWindowLayer: 0xc097e70>>

私はびっくりしています。UIWindow のサイズが正しくない理由は何ですか?

4

2 に答える 2

8

これは非常によくある問題です: UIButton がそのスーパービューの 1 つの境界の外にあります。clipsToBounds/が NO (デフォルト) に設定されている場合masksToBounds、UIButton は引き続き表示されますが、タッチ イベントは送信されません。

このケースを単純化しましょう。次のコードを持つビュー コントローラーを想定します。

- (void) viewDidLoad
{
    [super viewDidLoad];

    UIColor *fadedRedColor  = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.25];
    UIColor *fadedBlueColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.25];

    CGRect containerFrame = CGRectMake(25,  25,  100, 100);
    CGRect buttonFrame    = CGRectMake(100, 100, 64,  44);

    UIView *container = [[UIView alloc] initWithFrame:containerFrame];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:@"Button" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [button setFrame:buttonFrame];
    [button addTarget:self action:@selector(_handleButton:) forControlEvents:UIControlEventTouchUpInside];

    [container setBackgroundColor:fadedRedColor];
    [button    setBackgroundColor:fadedBlueColor];

    [container addSubview:button];

    [[self view] addSubview:container];
}

- (void) _handleButton:(id)sender
{
    NSLog(@"Moooooo!");
}

次のようになります。 スクリーンショット 1

ボタンは に含まれcontainerていますが、コンテナの境界の外にあります (コンテナは幅 100 ピクセル、高さ 100 ピクセルで、ボタンの原点は にあります100, 100)。

画面にタッチすると、UIKit はビュー階層 (UIWindow) の最上位から開始し-[UIView hitTest:withEvent:]、タッチを処理するビューが見つかるまで再帰的に呼び出します。ただし、この例では、UIKit がコンテナーに降りることはなく (境界の外側に触れたため)、ボタンのサブビューはヒットしません。

代わりに を に変更するbuttonFrame50, 50、次のようになります。 スクリーンショット 2

コンテナと重なっているボタンの部分は、タッチ イベントに応答します。コンテナーの外側にあるパーツは、次のことを行いません。

スクリーンショット 3

完全にタッチできないビューをデバッグするには、次のようなデバッグ機能を試すことができます。

static void sDebugViewThatIsntTouchable(UIView *view)
{
    UIView *superview = [view superview];

    while (superview) {
        CGRect rectInSuperview = [view convertRect:[view bounds] toView:superview];

        CGPoint topLeft     = CGPointMake(CGRectGetMinX(rectInSuperview), CGRectGetMinY(rectInSuperview));
        CGPoint topRight    = CGPointMake(CGRectGetMaxX(rectInSuperview), CGRectGetMinY(rectInSuperview));
        CGPoint bottomLeft  = CGPointMake(CGRectGetMinX(rectInSuperview), CGRectGetMaxY(rectInSuperview));
        CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rectInSuperview), CGRectGetMaxY(rectInSuperview));

        if (![superview pointInside:topLeft withEvent:nil]) {
            NSLog(@"Top left point of view %@ not inside superview %@", view, superview);
        }

        if (![superview pointInside:topRight withEvent:nil]) {
            NSLog(@"Top right point of view %@ not inside superview %@", view, superview);
        }

        if (![superview pointInside:bottomLeft withEvent:nil]) {
            NSLog(@"Bottom left point of view %@ not inside superview %@", view, superview);
        }

        if (![superview pointInside:bottomRight withEvent:nil]) {
            NSLog(@"Bottom right point of view %@ not inside superview %@", view, superview);
        }

        superview = [superview superview];
    }
};

編集: コメントで述べたように、犯人ビューはメインの UIWindow で、320x568 ではなく 320x480 にサイズ変更されました。 xib で「起動時にフルスクリーン」をオンにすると、これが修正されました

もちろん、問題は「なぜ?」です。:)

xib ファイルをテキスト エディターで開くと、幅 320、高さ 480 がウィンドウにハードコーディングされていることがわかります。起動時に xib がデコードされると、ウィンドウは最初にこの 320x480 フレームで構築されます。

次に、UIKit がクエリを実行-[UIWindow resizesToFullScreen]します (プライベート メソッド)。これが YES を返す場合、UIWindow は と同等の処理を行い[self setFrame:[[self window] bounds]]ます。

Interface Builder で「起動時に全画面表示」フラグを切り替えると、プライベートUIWindow.resizesToFullScreenフラグが直接切り替えられます。

于 2013-09-30T01:51:46.930 に答える