在 iOS 界面開發中,小尺寸控件(如關閉按鈕、圖標按鈕)常因點擊區域過小導致用戶操作困難,特別是單手操作大屏手機時。Apple 人機交互指南建議最小點擊區域為 44×44 pt,但設計稿往往要求更小的視覺尺寸。
通過重寫 UIView 的 hitTest:withEvent: 方法,可在不改變視圖視覺大小的前提下,擴大其觸摸響應區域。此方法是觸摸事件分發的入口,返回非 nil 視圖即表示該視圖可處理事件。
@interface ExpandedHitTestView : UIView
@property (nonatomic) UIEdgeInsets hitTestEdgeInsets; // 擴展邊距:正值向外擴大
@end
@implementation ExpandedHitTestView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_hitTestEdgeInsets = UIEdgeInsetsZero;
}
return self;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 如果視圖不可見、隱藏、透明度極低或禁用交互,直接返回 nil
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha < 0.01) {
return nil;
}
// 計算擴展後的邊界框
CGRect expandedBounds = CGRectInset(self.bounds,
-self.hitTestEdgeInsets.left,
-self.hitTestEdgeInsets.top);
expandedBounds = CGRectInset(expandedBounds,
-self.hitTestEdgeInsets.right,
-self.hitTestEdgeInsets.bottom);
// 判斷觸摸點是否在擴展區域內
if (CGRectContainsPoint(expandedBounds, point)) {
// 遞歸檢查子視圖(從後往前,上層優先)
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitView = [subview hitTest:convertedPoint withEvent:event];
if (hitView) return hitView;
}
return self;
}
return nil;
}
@end
// 為關閉按鈕擴大 20pt 點擊區域
UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
closeButton.frame = CGRectMake(0, 0, 24, 24); // 視覺尺寸 24×24
[closeButton setImage:[UIImage imageNamed:@"icon_close"] forState:UIControlStateNormal];
// 關鍵:設置擴展邊距(上下左右各擴展 20pt)
closeButton.hitTestEdgeInsets = UIEdgeInsetsMake(-20, -20, -20, -20);
| 方案 | 優點 | 缺點 |
|---|---|---|
| 重寫 hitTest | 不影響佈局、不增加視圖層級、性能最優 | 需子類化 UIView |
| 透明背景圖擴大 | 無需代碼、直接在圖片資源處理 | 增加圖片體積、難以動態調整 |
| 父視圖添加手勢 | 靈活控制 | 事件分發複雜、易衝突 |
| 增大 frame + 裁剪 | 簡單直接 | 影響佈局、可能遮擋鄰近視圖 |
UIButton 分類或基類 ExpandedHitTestButton,項目中統一使用。hitTestEdgeInsets 為負值表示向外擴展(如 UIEdgeInsetsMake(-20, -20, -20, -20))。UIEdgeInsetsZero 默認值,不影響無需擴展的控件。