Apple的iOS人機交互設計指南中指出,按鈕點擊熱區應不小於44x44pt,否則這個按鈕就會讓用戶覺得“很難用”,因為明明點擊上去了,卻沒有任何響應。
但我們有時做自定義Button的時候,設計圖上的給出按鈕尺寸明顯要小於這個數。例如我之前做過的自定義Slider上的Thumb只有12x12pt,做出來后我發現自己根本點不到按鈕……
這個問題在WWDC 2012 Session 216視頻中提到了一種解決方式。它重寫了按鈕中的pointInside方法,使得按鈕熱區不夠44×44大小的先自動縮放到44×44,再判斷觸摸點是否在新的熱區內。
//官方在視頻中給出的示例源碼 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)withEvent { CGFloat widthDelta = 44.0 - bounds.size.width; CGFloat heightDelta = 44.0 - bounds.size.height; bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta); return CGRectContainsPoint(bounds, point); }
Apple的iOS人機交互設計指南中指出,按鈕點擊熱區應不小於44x44pt,否則這個按鈕就會讓用戶覺得“很難用”,因為明明點擊上去了,卻沒有任何響應。
但我們有時做自定義Button的時候,設計圖上的給出按鈕尺寸明顯要小於這個數。例如我之前做過的自定義Slider上的Thumb只有12x12pt,做出來后我發現自己根本點不到按鈕……
這個問題在WWDC 2012 Session 216視頻中提到了一種解決方式。它重寫了按鈕中的pointInside方法,使得按鈕熱區不夠44×44大小的先自動縮放到44×44,再判斷觸摸點是否在//官方在視頻中給出的示例源碼
不過這里有兩個小問題:
-
當定義的Button.frame大於44×44時,這里仍然會將熱區縮小至44×44,從而導致超過44×44的按鈕熱區失去響應。
-
bounds變量未定義
修正后的代碼如下:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event { CGRect bounds = self.bounds; //若原熱區小於44x44,則放大熱區,否則保持原大小不變 CGFloat widthDelta = MAX(44.0 - bounds.size.width, 0); CGFloat heightDelta = MAX(44.0 - bounds.size.height, 0); bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta); return CGRectContainsPoint(bounds, point); }
二: 運行時模式 UIButton+LP
// UIButton+LP.h
#import <UIKit/UIKit.h>
@interface UIButton (LP)
@property(nonatomic,assign)CGFloat enlargedEdge;
//-(void)setEnlargedEdge:(CGFloat)enlargedEdge;
//-(float)enlargeEdge;
-(void)setEnlargedEdgeWithTop:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right;
@end
// UIButton+LP.m
#import "UIButton+LP.h"
#import <objc/runtime.h>
@implementation UIButton (LP)
staticchar topEdgeKey;
staticchar leftEdgeKey;
staticchar bottomEdgeKey;
staticchar rightEdgeKey;
-(void)setEnlargedEdge:(CGFloat)enlargedEdge
{
[selfsetEnlargedEdgeWithTop:enlargedEdge left:enlargedEdge bottom:enlargedEdge right:enlargedEdge];
}
-(float)enlargeEdge
{
return [(NSNumber *)objc_getAssociatedObject(self, &topEdgeKey) floatValue];
}
-(void)setEnlargedEdgeWithTop:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right
{
objc_setAssociatedObject(self, &topEdgeKey, [NSNumbernumberWithFloat:top], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, &leftEdgeKey, [NSNumbernumberWithFloat:left], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, &bottomEdgeKey, [NSNumbernumberWithFloat:bottom], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, &rightEdgeKey, [NSNumbernumberWithFloat:right], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(CGRect)enlargedRect
{
NSNumber * topEdge = objc_getAssociatedObject(self, &topEdgeKey);
NSNumber * leftEdge = objc_getAssociatedObject(self, &leftEdgeKey);
NSNumber * bottomEdge = objc_getAssociatedObject(self, &bottomEdgeKey);
NSNumber * rightEdge = objc_getAssociatedObject(self, &rightEdgeKey);
if(topEdge && leftEdge && bottomEdge && rightEdge){
CGRect enlargedRect = CGRectMake(self.bounds.origin.x - leftEdge.floatValue, self.bounds.origin.y - topEdge.floatValue, self.bounds.size.width + rightEdge.floatValue + leftEdge.floatValue ,self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
return enlargedRect;
}else{
returnself.bounds;
}
}
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if(self.alpha <= 0.01 || !self.userInteractionEnabled ||self.hidden){
returnnil;
}
CGRect enlargedRect = [selfenlargedRect];
returnCGRectContainsPoint(enlargedRect, point)?self:nil;
}
@end