これは非常によくある問題です: 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!");
}
次のようになります。
ボタンは に含まれcontainer
ていますが、コンテナの境界の外にあります (コンテナは幅 100 ピクセル、高さ 100 ピクセルで、ボタンの原点は にあります100, 100
)。
画面にタッチすると、UIKit はビュー階層 (UIWindow) の最上位から開始し-[UIView hitTest:withEvent:]
、タッチを処理するビューが見つかるまで再帰的に呼び出します。ただし、この例では、UIKit がコンテナーに降りることはなく (境界の外側に触れたため)、ボタンのサブビューはヒットしません。
代わりに を に変更するbuttonFrame
と50, 50
、次のようになります。
コンテナと重なっているボタンの部分は、タッチ イベントに応答します。コンテナーの外側にあるパーツは、次のことを行いません。
完全にタッチできないビューをデバッグするには、次のようなデバッグ機能を試すことができます。
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
フラグが直接切り替えられます。