使用 Runtime 禁止 UIAlertController
2019-07-05 本文已影响0人
ft6206
项目里需要增加强制更新功能,此时使用的是系统的UIAlertController,结果点击跳转到App Store之后,弹框就消失了,这是因为系统默认会自己dismiss,所以需要自己加个变量可以控制是否dismiss。
@dynamicft_rejectDismiss;
- (void)setFt_rejectDismiss:(BOOL)ft_rejectDismiss {
objc_setAssociatedObject(self, @selector(ft_rejectDismiss), @(ft_rejectDismiss), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)ft_rejectDismiss {
return [(NSNumber *)objc_getAssociatedObject(self, _cmd) boolValue];
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Classclass = [selfclass];
SEL originalSelector = NSSelectorFromString(@"_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:");
SELswizzledSelector =@selector(ft_dismissAnimated:
triggeringAction:
triggeredByPopoverDimmingView:
dismissCompletion:);
MethodoriginalMethod =class_getInstanceMethod(class, originalSelector);
MethodswizzledMethod =class_getInstanceMethod(class, swizzledSelector);
// 动态添加方法,如果类中不存在这个方法的实现,则添加成功
// 这里 UIAlertController 类中存在 originalMethod,所以添加是失败的
BOOLdidAddMethod =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{
// 交换两个方法的实现
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)ft_dismissAnimated:(BOOL)animation
triggeringAction:(UIAlertAction*)action
triggeredByPopoverDimmingView:(id)view
dismissCompletion:(id)handler {
// 如果点击“取消”按钮或者允许弹框 dismiss,就调用原来的方法(originalMethod)
// 因为已经交换了两个方法的实现,所以其实是调用 swizzledMethod
// 所以这里并不会出现循环调用
// 否则就忽略原来的方法(originalMethod),直接下一步,掉用后面的方法
if (action.style == UIAlertActionStyleCancel || self.ft_rejectDismiss == NO) {
[self ft_dismissAnimated:animation
triggeringAction:action
triggeredByPopoverDimmingView:view
dismissCompletion:handler];
}else{
SEL invokeHandler = NSSelectorFromString(@"_invokeHandlersForAction:");
// 这里如果使用 performSelector 来调 invokeHandler 这个方法
// [self performSelector:invokeHandler withObject:action];
// 会报 "PerformSelector may cause a leak because its selector is unknown" 的警告
// 为消除警告,用下面的方法
IMPimp = [selfmethodForSelector:invokeHandler];
void(*func)(id,SEL,UIAlertAction*) = (void*)imp;
func(self, invokeHandler, action);
}
}