Skip to content

Commit

Permalink
Cache last hit testing result to improve performance
Browse files Browse the repository at this point in the history
  • Loading branch information
ole committed Jun 14, 2012
1 parent 2babd39 commit 956b9ac
Showing 1 changed file with 72 additions and 6 deletions.
78 changes: 72 additions & 6 deletions OBShapedButton/OBShapedButton.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,43 @@ of this software and associated documentation files (the "Software"), to deal
#import "UIImage+ColorAtPixel.h"


@interface OBShapedButton ()

@property (nonatomic, assign) CGPoint previousTouchPoint;
@property (nonatomic, assign) BOOL previousTouchHitTestResponse;

- (void)resetHitTestCache;

@end


@implementation OBShapedButton

- (BOOL)isAlphaVisibleAtPoint:(CGPoint)point forImage:(UIImage *)image
@synthesize previousTouchPoint = _previousTouchPoint;
@synthesize previousTouchHitTestResponse = _previousTouchHitTestResponse;

- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self resetHitTestCache];
}
return self;
}

- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self) {
[self resetHitTestCache];
}
return self;
}


#pragma mark - Hit testing

- (BOOL)isAlphaVisibleAtPoint:(CGPoint)point forImage:(UIImage *)image
{
// Correct point to take into account that the image does not have to be the same size
// as the button. See https://github.com/ole/OBShapedButton/issues/1
Expand All @@ -60,26 +93,59 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
return superResult;
}

// Don't check again if we just queried the same point
// (because pointInside:withEvent: gets often called multiple times)
if (CGPointEqualToPoint(point, self.previousTouchPoint)) {
return self.previousTouchHitTestResponse;
} else {
self.previousTouchPoint = point;
}

// We can't test the image's alpha channel if the button has no image. Fall back to super.
UIImage *buttonImage = [self imageForState:UIControlStateNormal];
UIImage *buttonBackground = [self backgroundImageForState:UIControlStateNormal];

BOOL response = NO;

if (buttonImage == nil && buttonBackground == nil) {
return YES;
response = YES;
}
else if (buttonImage != nil && buttonBackground == nil) {
return [self isAlphaVisibleAtPoint:point forImage:buttonImage];
response = [self isAlphaVisibleAtPoint:point forImage:buttonImage];
}
else if (buttonImage == nil && buttonBackground != nil) {
return [self isAlphaVisibleAtPoint:point forImage:buttonBackground];
response = [self isAlphaVisibleAtPoint:point forImage:buttonBackground];
}
else {
if ([self isAlphaVisibleAtPoint:point forImage:buttonImage]) {
return YES;
response = YES;
} else {
return [self isAlphaVisibleAtPoint:point forImage:buttonBackground];
response = [self isAlphaVisibleAtPoint:point forImage:buttonBackground];
}
}

self.previousTouchHitTestResponse = response;
return response;
}


// Reset the Hit Test Cache when a new image is assigned to the button
- (void)setImage:(UIImage *)image forState:(UIControlState)state
{
[super setImage:image forState:state];
[self resetHitTestCache];
}

- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state
{
[super setBackgroundImage:image forState:state];
[self resetHitTestCache];
}

- (void)resetHitTestCache
{
self.previousTouchPoint = CGPointMake(CGFLOAT_MIN, CGFLOAT_MIN);
self.previousTouchHitTestResponse = NO;
}

@end

0 comments on commit 956b9ac

Please sign in to comment.