UIButton prevent continuous clicks

1 demand

There was a app in front of me, then I was crazy for each button click, results there can not be described in the BUG, the app is actually our own development, so this BUG repair is imminent.

2 Principle

Break points in response to button in the call stack every time through this method:
– (void) sendAction: (SEL) action to: (nullable ID) target forEvent: (nullable UIEvent * event
) so the basic idea is to have sendAction every time to decide if the time stamp, and time at last send are too short, otherwise it terminates the send, let it continue to send.
but related to a problem:
– (void) sendAction: (SEL) action to: (nullable ID) target forEvent: (nullable UIEvent * event) is a systematic method, I can’t change. There are two ways to solve
the problem:
(1) UIButton inheritance, define a CustomButton class in this sub class to override the sendAction method.
… (2) operation Time function, replace the UIButton of the sendAction method

Way (1) to change the place too much, almost every Button to search, replace it, there are Xib in the inappropriate, so the way (2)

2 implementation

Talk about ways to replace the article online too much, no longer copy paste.

Direct code, and notes

UIButton+InsensitiveTouch.h

#import < UIKit/UIKit.h> @interface; UIButton (InsensitiveTouch) / / open UIButton anti continuous mode (void) + enableInsensitiveTouch; / / close UIButton anti continuous mode (void) + disableInsensitiveTouch; / / set up the minimum time difference (s), click on the row is not set the default value is 0.5s + (void) setInsensitiveMinTimeInterval: (NSTimeInterval) interval @end;

UIButton+InsensitiveTouch.m

#import "UIButton+InsensitiveTouch.h" #import < objc/runtime.h> static NSTimeInterval; / / the minimum time difference insensitiveMinTimeInterval = 0.5; / / static void native sendAction:to:forEvent: (*originalImplementation) (ID, SEL, SEL, ID, UIEvent *) = NULL; / / sendAction:to:forEvent: static void replacedImplementation replacement (ID object, SEL selector, SEL action, ID target. UIEvent *event); @implementation UIButton (InsensitiveTouch) + enableInsensitiveTouch (void) {/ / get the current "@selector (sendAction:to:forEvent:) Method methodNow" corresponding to Method = class_getInstanceMethod (self, @selector (sendAction:to:forEvent:)); / / get the current sendAction:to:forEvent: address IMP implementationNow = method_getImplementation (methodNow); / This address is already / replacedImplementation, that has been replaced after if (implementationNow = = replacedImplementation (IMP)) {return}; / / save native sendAction:to:forEvent: address originalImplementation = (void (*) (ID, SEL, SEL, ID, UIEvent * implementationNow char *type; const)) = method_getTypeEncoding (methodNow); / / will achieve replacement for replacedImplementation class_replaceMethod (self, @selector (sendAction:to:forEvent:), (IMP) replacedImplementation, type (void));} + {IMP disableInsensitiveTouch implementationNow = class_getMethodImplementation (self, @selector (sendAction:to:forEvent:) if (originalImplementation); & & implementationNow = replacedImplementation (IMP)) {class_re PlaceMethod (self, @selector (sendAction:to:forEvent:), (IMP) originalImplementation, NULL);}} + (void) setInsensitiveMinTimeInterval: (NSTimeInterval interval) {insensitiveMinTimeInterval = interval;} - {(NSTimeInterval) lastTouchTimestamp return [objc_getAssociatedObject (self, @selector doubleValue] (lastTouchTimestamp));} - (void) setLastTouchTimestamp: (NSTimeInterval) {objc_setAssociatedObject timestamp (self, @selector (lastTouchTimestamp), @ (timestamp), OBJC_ASSOCIATION_RETAIN_NONATOMIC);} / / @end static void replacedImplementation replaced sendAction:to:forEvent: (ID object, SEL selector, SEL action, ID target, UIEvent *event) {/ / is the button, and the UIEventTypeTouches event, only the time stamp judgment / / but To exclude the two button "CUShutterButton" and "CAMShutterButton", the two are 8, 10 system camera button class. This is the two special package over the button, if they are events with a time stamp to filter out, you will find the app popup behind the camera to press to take photos. If ([object isKindOfClass:UIButton.self] & & [NSStringFromClass ([object class])! IsEqualToString:@ "CUShutterButton" & & [NSStringFromClass ([object class])! IsEqualToString:@ "CAMShutterButton" & & event.type = = UIEventTypeTouches) {/ / *button = UIButton for the timestamp judgment (UIButton *) object; if (ABS (event.timestamp - button.lastTouchTimestamp) < insensitiveMinTimeInterval) {/ / time is too short, in return, the incident also suspended Send return}; button.lastTouchTimestamp = event.timestamp;} / / time stamp on no problem, do not belong to the quick click on the if (originalImplementation) {/ / call system to achieve the primary, continue to complete the event Send (object originalImplementation, sele Ctor, action, target, event);}}

3 use

Call [UIButton enableInsensitiveTouch] in Appdelegate launchWith… Most of the implementation of the Internet like in (+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) (void) load method to complete the replacement, so the use of the library when there is no need to call, but I still feel that users know what they do better.