使用 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);

    }

}

上一篇下一篇

猜你喜欢

热点阅读