日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用runtime解决棘手问题锦集(持续更新)

發(fā)布時(shí)間:2025/3/20 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用runtime解决棘手问题锦集(持续更新) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

寫在最前面:本文章只是記錄自己再項(xiàng)目中遇到的問題,并用runtime

?

1.項(xiàng)目需要按鈕觸發(fā)范圍比原來布局大,該如何實(shí)現(xiàn)?

首先想到的是改變按鈕的大小,這個(gè)方法是最基礎(chǔ)的方法來根本上解決按鈕范圍問題。可是有的項(xiàng)目一個(gè)按鈕貼著一個(gè)按鈕,但是只能中間高亮的按鈕才能被點(diǎn)擊,大小固定無法改變。這種情況下無法通過改變布局來增加點(diǎn)擊范圍,我們可以通過runtime運(yùn)行時(shí)機(jī)制來動(dòng)態(tài)增加按鈕的可點(diǎn)擊范圍。具體代碼如下:

#import "UIButton+MQIntervalClickButton.h"

#import <objc/runtime.h>

?

// 按鈕點(diǎn)擊間隔時(shí)間

static char* const intervalClickTimeKey = "intervalClickTimeKey";

static char* const canClickButtonKey? ? = "canClickButtonKey";

?

// 按鈕點(diǎn)擊可擴(kuò)大范圍

static char* const expandHitFloatKey? ? = "expandHitFloatKey";

?

@interface UIButton ()

?

// 是否可響應(yīng)點(diǎn)擊事件 YES:不會(huì)響應(yīng)點(diǎn)擊事件 NO:會(huì)響應(yīng)點(diǎn)擊事件

@property (nonatomic, assign) BOOL canClickButton;

?

@end

?

@implementation UIButton (MQIntervalClickButton)

?

#pragma mark - Action

// 交換后按鈕的點(diǎn)擊事件

- (void)mq_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event

{

? ? if (!self.canClickButton) {

?? ? ? ?

//? ? ? ? // 默認(rèn)間隔時(shí)間為3

//? ? ? ? self.intervalClickTime = self.intervalClickTime == 0?3 : self.intervalClickTime;

? ? ? ? // 第一次執(zhí)行點(diǎn)擊事件后設(shè)置是否可點(diǎn)擊屬性為YES

? ? ? ? self.canClickButton = YES;

? ? ? ? [self mq_sendAction:action to:target forEvent:event];

? ? ? ? // 延遲間隔時(shí)間設(shè)置是否可點(diǎn)擊屬性NO

? ? ? ? [self performSelector:@selector(setCanClickButton:) withObject:@(NO) afterDelay:self.intervalClickTime];

? ? }

}

?

// 重寫方法-點(diǎn)擊是否在可響應(yīng)范圍內(nèi)

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

{

? ? if (self.expandHitFloat) {

?? ? ? ?

? ? ? ? CGRect buttonFrame = self.bounds;

? ? ? ? // CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)是以rect為中心,根據(jù)dx和dy來實(shí)現(xiàn)縮小 :正值表示縮小,負(fù)值表示擴(kuò)大

? ? ? ? CGRect hitFrame = CGRectInset(buttonFrame, self.expandHitFloat, self.expandHitFloat);

?? ? ? ?

? ? ? ? return CGRectContainsPoint(hitFrame, point);

? ? } else {

?? ? ? ?

? ? ? ? return [super pointInside:point withEvent:event];

? ? }

}

?

#pragma mark - setter & getter

?

// runtime添加按鈕點(diǎn)擊間隔時(shí)間

- (NSTimeInterval)intervalClickTime

{

? ? return [objc_getAssociatedObject(self, intervalClickTimeKey) doubleValue];

}

?

- (void)setIntervalClickTime:(NSTimeInterval)intervalClickTime

{

? ? objc_setAssociatedObject(self, intervalClickTimeKey, @(intervalClickTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

?

// runtime添加按鈕是否可點(diǎn)擊

- (BOOL)canClickButton

{

? ? return [objc_getAssociatedObject(self, canClickButtonKey) doubleValue];

}

?

- (void)setCanClickButton:(BOOL)canClickButton

{

? ? objc_setAssociatedObject(self, canClickButtonKey, @(canClickButton), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

?

// runtime添加點(diǎn)擊擴(kuò)大范圍

- (CGFloat)expandHitFloat

{

? ? return [objc_getAssociatedObject(self, expandHitFloatKey) floatValue];

}

?

- (void)setExpandHitFloat:(CGFloat)expandHitFloat

{

? ? objc_setAssociatedObject(self, expandHitFloatKey, @(expandHitFloat), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

@end

?

2.使用系統(tǒng)提示框UIAlertController彈出信息,點(diǎn)擊確定又不讓它消失。沒有做修改的情況下,點(diǎn)擊確定或者取消UIAlertController直接消失,需求需要點(diǎn)擊確認(rèn)之后用輸入框的信息去后臺(tái)比較反饋之后,如果信息和后臺(tái)相符則消失如果不符合則不消失彈出toast提示錯(cuò)誤并UIAlertController

這種情況使用runtime黑科技很容易實(shí)現(xiàn),首先用在提示框彈出的時(shí)候獲取到@“_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:”系統(tǒng)消失方法,添加一個(gè)控制消失與否的BOOL值z(mì)je_rejectDismiss用來判斷什么時(shí)候可以消失,具體代碼如下:

.h

#import <UIKit/UIKit.h>

?

@interface UIAlertController (HPHDismiss)

@property (nonatomic, assign) BOOL zje_rejectDismiss;

@end

?

.m

#import "UIAlertController+HPHDismiss.h"

#import <objc/runtime.h>

?

@implementation UIAlertController (HPHDismiss)

- (void)setZje_rejectDismiss:(BOOL)zje_rejectDismiss

{

? ? objc_setAssociatedObject(self, @selector(zje_rejectDismiss), @(zje_rejectDismiss), OBJC_ASSOCIATION_ASSIGN);

}

?

- (BOOL)zje_rejectDismiss

{

? ? return [(NSNumber *)objc_getAssociatedObject(self, _cmd) boolValue];

}

?

+ (void)load {

? ? static dispatch_once_t onceToken;

? ? dispatch_once(&onceToken, ^{

? ? ? ? Class class = [self class];

?? ? ? ?

? ? ? ? SEL originalSelector = NSSelectorFromString(@"_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:");

? ? ? ? SEL swizzledSelector = @selector(zje_rejectDismiss:

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? triggeringAction:

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? triggeredByPopoverDimmingView:

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dismissCompletion:);

?? ? ? ?

? ? ? ? Method originalMethod = class_getInstanceMethod(class, originalSelector);

? ? ? ? Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

?? ? ? ?

? ? ? ? //? ? ? ? 動(dòng)態(tài)添加方法,如果類中不存在這個(gè)方法的實(shí)現(xiàn),則添加成功

? ? ? ? //? ? ? ? 這里 UIAlertController 類中存在 originalMethod,所以添加是失敗的

? ? ? ? BOOL didAddMethod = class_addMethod(class,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? originalSelector,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? method_getImplementation(swizzledMethod),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? method_getTypeEncoding(swizzledMethod));

? ? ? ? if (didAddMethod) {

? ? ? ? ? ? // 如果添加成功,則用 originalMethod 替換添加的空方法 originalMethod

? ? ? ? ? ? class_replaceMethod(class,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? swizzledSelector,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? method_getImplementation(originalMethod),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? method_getTypeEncoding(originalMethod));

? ? ? ? } else {

? ? ? ? ? ? // 交換兩個(gè)方法的實(shí)現(xiàn)

? ? ? ? ? ? method_exchangeImplementations(originalMethod, swizzledMethod);

? ? ? ? }

? ? });

}

- (void) zje_rejectDismiss:(BOOL)animation

? ? ? ? ? triggeringAction:(UIAlertAction *)action

triggeredByPopoverDimmingView:(id)view

?? ? ? ? dismissCompletion:(id)handler {

? ? //? ? 如果點(diǎn)擊“取消”按鈕或者允許彈框 dismiss,就調(diào)用原來的方法(originalMethod)

? ? //? ? ? 因?yàn)橐呀?jīng)交換了兩個(gè)方法的實(shí)現(xiàn),所以其實(shí)是調(diào)用 swizzledMethod

? ? //? ? ? 所以這里并不會(huì)出現(xiàn)循環(huán)調(diào)用

? ? //? ? 否則就忽略原來的方法(originalMethod),直接下一步,掉用后面的方法

? ? if (action.style == UIAlertActionStyleCancel || self.zje_rejectDismiss == NO) {

? ? ? ? [self zje_rejectDismiss:animation

?? ? ? ? ? ? ? triggeringAction:action

? triggeredByPopoverDimmingView:view

? ? ? ? ? ? ? dismissCompletion:handler];

? ? } else {

? ? ? ? SEL invokeHandler = NSSelectorFromString(@"_invokeHandlersForAction:");

? ? ? ? //? ? ? ? 這里如果使用 performSelector 來調(diào) invokeHandler 這個(gè)方法

? ? ? ? //? ? ? ? ? [self performSelector:invokeHandler withObject:action];

? ? ? ? //? ? ? ? 會(huì)報(bào) "PerformSelector may cause a leak because its selector is unknown" 的警告

? ? ? ? //? ? ? ? 為消除警告,用下面的方法

? ? ? ? IMP imp = [self methodForSelector:invokeHandler];

? ? ? ? void (*func)(id, SEL, UIAlertAction *) = (void *)imp;

? ? ? ? func(self, invokeHandler, action);

? ? }

}

@end

?

3.避免按鈕重復(fù)點(diǎn)擊。

.h

#import <UIKit/UIKit.h>

?

@interface UIControl (SingleTap)

?

@property (nonatomic, assign) NSTimeInterval cjr_acceptEventInterval;// 可以用這個(gè)給重復(fù)點(diǎn)擊加間隔

?

.m

?

#import "UIControl+SingleTap.h"

#import <objc/runtime.h>

?

?

@implementation UIControl (SingleTap)

?

static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval";

static const char *UIControl_acceptEventTime = "UIControl_acceptEventTime";

?

- (NSTimeInterval )cjr_acceptEventInterval{

? ? return [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue];

}

?

- (void)setCjr_acceptEventInterval:(NSTimeInterval)cjr_acceptEventInterval{

? ? objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(cjr_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

?

- (NSTimeInterval )cjr_acceptEventTime{

? ? return [objc_getAssociatedObject(self, UIControl_acceptEventTime) doubleValue];

}

?

- (void)setCjr_acceptEventTime:(NSTimeInterval)cjr_acceptEventTime{

? ? objc_setAssociatedObject(self, UIControl_acceptEventTime, @(cjr_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

?

+ (void)load{

? ? //獲取著兩個(gè)方法

? ? Method systemMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));

? ? SEL sysSEL = @selector(sendAction:to:forEvent:);

?? ?

? ? Method myMethod = class_getInstanceMethod(self, @selector(cjr_sendAction:to:forEvent:));

? ? SEL mySEL = @selector(cjr_sendAction:to:forEvent:);

?? ?

? ? //添加方法進(jìn)去

? ? BOOL didAddMethod = class_addMethod(self, sysSEL, method_getImplementation(myMethod), method_getTypeEncoding(myMethod));

?? ?

? ? //如果方法已經(jīng)存在了

? ? if (didAddMethod) {

? ? ? ? class_replaceMethod(self, mySEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));

? ? }else{

? ? ? ? method_exchangeImplementations(systemMethod, myMethod);

?? ? ? ?

? ? }

?? ? //----------以上主要是實(shí)現(xiàn)兩個(gè)方法的互換,load是gcd的只shareinstance,果斷保證執(zhí)行一次

}

?

- (void)cjr_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{

? ? if (NSDate.date.timeIntervalSince1970 - self.cjr_acceptEventTime < self.cjr_acceptEventInterval) {

? ? ? ? return;

? ? }

?? ?

? ? if (self.cjr_acceptEventInterval > 0) {

? ? ? ? self.cjr_acceptEventTime = NSDate.date.timeIntervalSince1970;

? ? }

?? ?

? ? [self cjr_sendAction:action to:target forEvent:event];

}

@end

?

?

@property (nonatomic, assign) NSTimeInterval cjr_acceptEventTime;// 間隔時(shí)間

?

@end

轉(zhuǎn)載于:https://www.cnblogs.com/jezhuang/p/10338446.html

與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的使用runtime解决棘手问题锦集(持续更新)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。