禁止UIAlertController的dimiss

UIAlertController是在iOS 8.0以后才出现的,用于弹窗提示的视图,在8.0 之前使用的是UIAlertView。UIAlertController有两种模式:Alert和Sheet。只有在Alert模式下,才可以向UIAlertController中添加UITextField,否则会崩溃。默认情况都是点击UIAlertController上的按钮之后,执行相应回调最后dismiss掉。在没有UITextField的时候,这样没有任何问题,但是在UITextField存在的情况下,且需要对输入进行合法性验证,只有输入合法时点击UIAlertController才进行回调处理。按照系统的默认方式在点击UIAlertController按钮时流程为:

  1. 如果输入合法,执行处理回调;如果输入不合法,给出提示,不执行回调。
  2. UIAlertController dismiss

这里不管输入合法与否,都是会dismiss,如何做到输入不合法,不dismiss,直到输入合法才dismiss。目前能想到的只有两种方法:

  1. 自己重写一个UIAlertController,好处是可以根据实际需求灵活配置,包括大小,颜色之类的,坏处是工作量比较大。
  2. 从系统UIAlertController入手,根据方法调用过程hook相关方法。

在UIAlertController回调里打一个断点,然后查看函数的调用栈:

从函数栈中猜想,系统在这个过程中应该是调用了两个关键方法。首先调用_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView: 将UIAlertController dismiss,接着调用_fireOffActionOnTargetIfValidForAction:来执行回调。

验证:

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        //在iOS 11.0以上UIAlertController的dimiss的私有方法是_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:
        //iOS 11.0以下是_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:
        SEL originalSelectorWitCompletion = NSSelectorFromString(@"_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:");
        SEL originalSelector = NSSelectorFromString(@"_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:");

        SEL swizzledSelector = @selector(def_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:);

        SEL swizzledSelectorWithCompletion = @selector(def_dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:);

        Method originalMethodWithCompletion = class_getInstanceMethod(class, originalSelec 大专栏  禁止UIAlertController的dimisstorWitCompletion);
        Method originalMethod = class_getInstanceMethod(class, originalSelector);

        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        Method swizzledMethodWithCompletion = class_getInstanceMethod(class, swizzledSelectorWithCompletion);
        if (originalMethodWithCompletion) {
            method_exchangeImplementations(originalMethodWithCompletion, swizzledMethodWithCompletion);
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)def_dismissAnimated:(BOOL)animation triggeringAction:(UIAlertAction *)action triggeredByPopoverDimmingView:(id)view dismissCompletion:(id)handler
{
    if (action.style == UIAlertActionStyleCancel) {
        [self def_dismissAnimated:animation triggeringAction:action triggeredByPopoverDimmingView:view dismissCompletion:handler];
    } else {
        if (self.validateBlock && self.textFields.count) {
            self.isDismiss = self.validateBlock(self.textFields.firstObject.text);
            if (self.isDismiss) {
                [self def_dismissAnimated:animation triggeringAction:action triggeredByPopoverDimmingView:view dismissCompletion:handler];
            }
        } else {
            [self def_dismissAnimated:animation triggeringAction:action triggeredByPopoverDimmingView:view dismissCompletion:handler];
        }
    }
}

- (void)def_dismissAnimated:(BOOL)animation triggeringAction:(UIAlertAction *)action triggeredByPopoverDimmingView:(id)view
{
    if (action.style == UIAlertActionStyleCancel) {
        [self def_dismissAnimated:animation triggeringAction:action triggeredByPopoverDimmingView:view];
    } else {
        if (self.validateBlock && self.textFields.count) {
            self.isDismiss = self.validateBlock(self.textFields.firstObject.text);
            if (self.isDismiss) {
                [self def_dismissAnimated:animation triggeringAction:action triggeredByPopoverDimmingView:view];
            }
        } else {
            [self def_dismissAnimated:animation triggeringAction:action triggeredByPopoverDimmingView:view];
        }
    }
}

- (void)setIsDismiss:(BOOL)isDismiss
{
    objc_setAssociatedObject(self, @selector(isDismiss), @(isDismiss), OBJC_ASSOCIATION_ASSIGN);
}

- (BOOL)isDismiss
{
    return [(NSNumber *)objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setValidateBlock:(DEFAlertControllerTextFieldValidateBlock)validateBlock
{
    objc_setAssociatedObject(self, @selector(validateBlock), validateBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (DEFAlertControllerTextFieldValidateBlock)validateBlock
{
    return objc_getAssociatedObject(self, @selector(validateBlock));
}

实际结果表明,上述分析是正确的。有一点需要注意的是,在iOS 11.0以后UIAlertController使用的私有API方法名和11.0以下使用的不一样。

原文地址:https://www.cnblogs.com/sanxiandoupi/p/11712906.html

时间: 2024-10-12 04:09:47

禁止UIAlertController的dimiss的相关文章

从 UIAlertView 到 UIAlertController

在 iOS8中,苹果提供了UIAlertController,以取代原有的 UIAlertView 和 UIActionSheet,以前的 UIAlertView是这样的: 1 UIAlertView *alertView = [[UIAlertView alloc] 2 initWithTitle:@"DefaultStyle" 3 message:@"the default alert view style" 4 delegate:self 5 cancelBu

防止恶意解析——禁止通过IP直接访问网站

一.什么是恶意解析 一般情况下,要使域名能访问到网站需要两步,第一步,将域名解析到网站所在的主机,第二步,在web服务器中将域名与相应的网站绑定.但是,如果通过主机IP能直接访问某网站,那么把域名解析到这个IP也将能访问到该网站,而无需在主机上绑定,也就是说任何人将任何域名解析到这个IP就能访问到这个网站.可能您并不介意通过别人的域名访问到您的网站,但是如果这个域名是未备案域名呢?一旦被查出,封IP.拔线甚至罚款的后果都是需要您来承担的.某些别有用心的人,通过将未备案域名解析到别人的主机上,使其

iOS关于UITabView和UIAlertController,UIAlertAction以及UINavigation,值修改的传递页面推送

关于UITabView和UIAlertController,UIAlertAction以及UINavigation,值修改的传递 集合嵌套集合的操作 声明 两个必须的的代理 实现部分代码 - (void)viewDidLoad { [super viewDidLoad]; // 创建一个TabView self.tabv = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped]; sel

avalon学习笔记ui篇-如何将avalon默认的amd模型禁止,以及用require重写。

一.如何禁止avalon自带的amd模型 1.采用avalon.shim.js这个文件,这个文件删除了原本自带的amd模型,不需要手动删除,修改. 2.打开avalon.js这个文件,搜索avalon.config,将true改为false. 二,下载text.js和css.js 1.因为avalonUI依赖了html文件和css文件. 2.并且将text.js和css.js,在配置中预加载 priority:['text','css'] 三.完整配置项 require.config({ //b

Nginx禁止直接通过IP地址访问网站(关闭默认站点或空主机头)

这篇文章主要介绍了Nginx中禁止使用IP访问网站的配置实例,一般在备案时可能需要这种设置,需要的朋友可以参考下 国内因为备案的原因,所有服务器都要禁止使用IP访问网站.否则,如果允许使用IP访问网站,那随便解析一个域名到该IP,访问该域名就可以打开网站了.这是一个极大的风险!Nginx中可以很方便的来解决这个问题,小菜鸟来跟大家一起探讨一下. 如下的配置项,可以设置允许使用IP访问网站. server { listen 80; server_name ""; } 这里相当于是绑定了一

No-Transform协议禁止搜索引擎转码兼移动站建设问答

No-Transform协议禁止搜索引擎转码兼移动站建设问答技术 maybe yes 发表于2014-12-29 13:42 原文链接 : http://blog.lmlphp.com/archives/51  来自 : LMLPHP后院 在 手机端访问时,发现自己的站点经常被百度,360等搜索引擎转码展示.转码后发现网页质量不太好,很多功能都出问题了.本人也是非常讨厌这种无声无息的动 作,之前在站长沙龙上看到过一些问答,有关于如何禁止搜索引擎转码网页的问题,需要加上一段代码.我就是后知后觉的那

Android禁止按键关闭AlertDialog

在Android系统中,默认点击AlertDialog中的按键都会关闭该AlertDialog,但有些情况下我们并不希望使对话框关闭,或者希望使对话框在自己需要的时候再关闭. 例如我现在做的一个项目,通过AlertDialog读取用户输入的一个值,希望只有在判断值为正确范围内才关闭该对话框,否则对话框予以保留,并给以用户相应的提醒. 可以利用反射的机制来实现这一效果: 不关闭对话框: ? 1 2 3 4 5 6 7 8 9 10 // 使对话框无法关闭 try {     Field field

js禁止微信浏览器下拉显示黑底查看网址,不影响内部Scroll

开发项目跑在微信浏览器经常会遇到一个问题,微信浏览器下拉的时候会出现自带的黑色底色(显示网址)如下图: 网上好多js禁止操作的做法禁止了内部Scroll,导致页面不能滚动,上拉加载失效,例如这种做法: $('body').on('touchmove', function (event) {event.preventDefault();}); or document.addEventListener('touchmove', function(e){e.preventDefault()}, fal

微信公众号禁止设置ngrok地址的解决办法

最近想改一下以前测试公众号的地址为ngrok2的亚洲节点,结果提示安全问题,禁止设置.原来是微信把类似花生壳ngrok等这些地址都封了. 现在暂时用的解决办法是在外网服务器上做一个转发程序,只需一个handler using System.Configuration; using System.Diagnostics; using System.Net; using System.Web; namespace WechatProxy { public class WechatHandler :