Created
January 16, 2022 14:07
-
-
Save theoknock/463d17942cb6b36ca97bc69e17b6843c to your computer and use it in GitHub Desktop.
Touch handling that reuses the same call regardless of touch phase, and also reuses the same UITouch object passed to touchesBegan throughout the entire touch sequence. Uses bitwise operators to simultaneously detect a given phase and set a button property.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static void (^(^(^touch_handler_init)(UIView *))(UITouch *))(void) = ^ (UIView * view) { | |
CGRect contextRect = view.bounds; | |
float minX = (float)CGRectGetMinX(contextRect); | |
float midX = (float)CGRectGetMidX(contextRect); | |
float mdnX = (float)(((int)midX & (int)minX) + (((int)midX ^ (int)minX) >> 1)); // Average of two floats | |
. | |
. | |
. | |
return ^ (UITouch * touch) { | |
static CGPoint touch_point; | |
static CGFloat touch_angle; | |
static unsigned int touch_property; | |
return ^{ | |
touch_point = [touch preciseLocationInView:view]; | |
touch_angle = atan2(touch_point.y - maxY, touch_point.x - maxX) * (180.0 / M_PI); | |
if (touch_angle < 0.0) touch_angle += 360.0; | |
touch_property = (unsigned int)round(rescale(touch_angle, 180.0, 270.0, 0.0, 4.0)); | |
filter(buttons)(^ (UIButton * _Nonnull button, unsigned int index) { | |
[button setSelected:!(UITouchPhaseEnded ^ touch.phase) & !(touch_property ^ button.tag)]; | |
[button setHighlighted:(UITouchPhaseEnded ^ touch.phase) & !(touch_property ^ button.tag)]; | |
[button setCenter:^{ | |
float angle = rescale(button.tag, 0, 4, 180.0, 270.0); | |
float radians = degreesToRadians(angle); | |
float radius = sqrt(pow(touch_point.x - maxX, 2.0) + | |
pow(touch_point.y - maxY, 2.0)); | |
radius = fmaxf(midX, fminf(radius, maxX)); | |
CGFloat x = maxX - radius * -cos(radians); | |
CGFloat y = maxY - radius * -sin(radians); | |
return CGPointMake(x, y); | |
}()]; | |
}); | |
}; | |
}; | |
}; | |
static void (^(^touch_handler)(UITouch *))(void); | |
static void (^handle_touch)(void); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
map(buttons)(^ UIButton * (unsigned int index) { | |
UIButton * button; | |
[button = [UIButton new] setTag:index]; | |
[button setImage:[UIImage systemImageNamed:@"questionmark.circle" withConfiguration:[[UIImageSymbolConfiguration configurationWithPointSize:42] configurationByApplyingConfiguration:[UIImageSymbolConfiguration configurationPreferringMulticolor]]] forState:UIControlStateNormal]; | |
[button setImage:[UIImage systemImageNamed:@"questionmark.circle.fill" withConfiguration:[[UIImageSymbolConfiguration configurationWithPointSize:42] configurationByApplyingConfiguration:[UIImageSymbolConfiguration configurationPreferringMulticolor]]] forState:UIControlStateHighlighted]; | |
[button setImage:[UIImage systemImageNamed:@"exclamationmark.circle.fill" withConfiguration:[[UIImageSymbolConfiguration configurationWithPointSize:42] configurationByApplyingConfiguration:[UIImageSymbolConfiguration configurationPreferringMulticolor]]] forState:UIControlStateSelected]; | |
[button sizeToFit]; | |
[button setUserInteractionEnabled:FALSE]; | |
void (^eventHandlerBlockTouchUpInside)(void) = ^{ | |
}; | |
objc_setAssociatedObject(button, @selector(invoke), eventHandlerBlockTouchUpInside, OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
[button addTarget:eventHandlerBlockTouchUpInside action:@selector(invoke) forControlEvents:UIControlEventTouchUpInside]; | |
[self.view addSubview:button]; | |
float angle = 270.0 - ((90.0 / 4.0) * button.tag); | |
[button setCenter:[[UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMaxX(self.view.bounds), CGRectGetMaxY(self.view.bounds)) radius:CGRectGetMidX(self.view.bounds) startAngle:degreesToRadians(angle) endAngle:degreesToRadians(angle) clockwise:FALSE] currentPoint]]; | |
return button; | |
}); | |
touch_handler = touch_handler_init(self.view); | |
} | |
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | |
// set once per touch | |
dispatch_barrier_async(dispatch_get_main_queue(), ^{ (handle_touch = touch_handler(touches.anyObject))(); }); | |
} | |
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | |
// reuses UITouch object temporarily retained, above | |
dispatch_barrier_async(dispatch_get_main_queue(), ^{ handle_touch(); }); | |
} | |
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | |
// reuses UITouch object temporarily retained, above | |
dispatch_barrier_async(dispatch_get_main_queue(), ^{ handle_touch(); }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment