首页 > 其他分享 >通过UIApplicationMain实现应用内多种事件拦截

通过UIApplicationMain实现应用内多种事件拦截

时间:2023-04-12 18:09:18浏览次数:49  
标签:sendAction sendEvent UIEvent UIApplication 应用 touch 拦截 event UIApplicationMain

简介

UIApplicationMain 大家并不陌生,因为在通过 XCode 建立 iOS 的 Ojective-C 工程时肯定会看到。新建的 main.m 文件长这样:

int main(int argc, char * argv[]) {
    NSString* appDelegateClassName;
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

大部分人都不会在意这个函数,其实它非常有用。

UIApplicationMain 的参数分 4 个部分,argc,argv,principalClassName 和 delegateClassName 。

argc 和 argv 不需要介绍,是 C 语言 main 函数的基础参数。后两个参数都是 NSString 类型的参数。也是着重要介绍的参数。

delegateClassName 参数比较好理解,即设置 App 的公用回调函数,例如如下函数就是常用的应用完成启动时的回调:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

新建 XCode 工程时已经替开发者新建了 AppDelegate 类,但是因为后来苹果将应用回调的大部分功能转移到了 SceneDelegate 类中,所以 AppDelegate 的作用越来越小了。

注:之所以 AppDelegate 被替代,原因是 iOS 13 之后,苹果引入了多场景的概念,不同的场景对应不同的回调,而传统的 AppDelegate 不适应这种回调模式。

principalClassName 是最重要的参数,它需要开发者提供 UIApplication 或者它的子类,如果传入 nil,则默认使用 UIApplication 。

传入自定义 principalClassName

如果不用 UIApplication 作为默认的 principalClassName,而是传入它的子类,一般是为了解决产品在应用层面的管理问题。例如设计了一个 UIApplication 的子类叫 MyApplication,那么,原先的 main.m 代码就需要按如下方式重写:

int main(int argc, char * argv[]) {
    NSString* appDelegateClassName;
    NSString* applicationClassName;
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        applicationClassName = NSStringFromClass([MyApplication class]);
    }
    return UIApplicationMain(argc, argv, applicationClassName, appDelegateClassName);
}

除此之外还有另一个办法来告诉项目工程自己需要用别的类替代,其方法是在 Info.plist 文件中引入 Principal class 作为 Key 的字符串键值,其值填入类名,例如本例的 MyApplication,那么,当 UIApplicationMain 所传入的 principalClassName 参数为 nil 时,会自动在 Info.plist 寻找 Principal class 的值来作为 applicationClassName。

拦截应用内的 UIEvent 事件

实现 UIApplication 子类可以从应用层面全局监控所有的 UIEvent 事件。UIEvent 事件包括用户的输入,传感器等信息,最常处理的是用户输入。

例如通过以下方式继承实现 sendEvent 后,可以在产品内观察记录所有用户的手指在屏幕上移动的事件。并记录下触摸的位置。

- (void)sendEvent: (UIEvent*)event {
    if (event.type == UIEventTypeTouches) {
        NSSet<UITouch*>* touches = [event allTouches];
        UITouch* touch = touches.objectEnumerator.nextObject;
        if (touch) {
            if (touch.phase == UITouchPhaseMoved) {
                UIView* view = touch.view;
                CGPoint point = [touch locationInView: view];
                NSLog(@"detect move event (%lf, %lf)", point.x, point.y);
            }
        }
    }
    [super sendEvent: event];
}

因为 sendEvent 是管理着 UIEvent 的最顶层下发,这就意味着在继承实现 sendEvent 时不调用 [super sendEvent: event] 的话,UIEvent 就不会往下传递,这样就能起到拦截用户输入使它不生效的效果。

例如,以下代码可以起到“屏蔽所有用户触摸事件”的效果。

- (void)sendEvent: (UIEvent*)event {
    if (event.type == UIEventTypeTouches) {
        NSSet<UITouch*>* touches = [event allTouches];
        UITouch* touch = touches.objectEnumerator.nextObject;
        if (touch) {
            // 屏蔽所有用户触摸事件
            return;
        }
    }
    [super sendEvent: event];
}

监控应用内的 Action

另一个常用的方法是 sendAction:

- (BOOL)sendAction: (SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event {
    NSLog(@"send action");
    return [super sendAction: action to: target from: sender forEvent: event];
}

Action 是 Event 管理下的封装,也就是说是先有事件,才可能出现Action 。

给 UIButton 通过 addTarget 添加的动作就是 Action,通过继承实现 sendAction 可以检测到整个应用内的所有 Action 事件。

这里要注意,不能通过给 sendAction 返回 NO 来屏蔽应用内的点击响应,除非在继承实现时不调用 super 的 sendAction 。

处理应用跳转请求

[UIApplication.sharedApplication openURL: url 
                                 options: @{} 
                       completionHandler: nil];

以上这段代码大家并不陌生了,应用跳转,打开内置浏览器等都会调用这个函数。

通过继承实现 openURL 可以拦截所有 openURL 调用,这样做最大的好处是可以进行 URL 选择过滤,并且可以实现根据需要弹窗提醒用户即将进行跳转。

拦截的策略和之前 sendEvent,sendAction 一样,不需要过多赘述。

移动开发者联盟加入指引

标签:sendAction,sendEvent,UIEvent,UIApplication,应用,touch,拦截,event,UIApplicationMain
From: https://blog.51cto.com/u_15493151/6185987

相关文章

  • MBI5253GFN-A专为LED视频应用而设计的16通道PWM恒流LED驱动器芯片
    MBI5253GFN-A是为使用内部脉宽调制(PWM)控制的LED视频应用而设计的,具有可选择的14位/13位色深。MBI5253具有一个16位移位寄存器,它将串行输入数据转换为输出端口的每个像素灰度。16个调节电流端口设计用于提供均匀和恒定的电流接收器,以驱动具有广泛VF变化范围的LED。输出电流可以通过......
  • 在电子文档管理系统中应用鱼群算法的优势
    鱼群算法是一种基于自然界中鱼群行为的计算机算法,可以用于优化问题的解决。在电子文档管理系统中,鱼群算法可以用来管理和优化文档的检索和分类。 通过鱼群算法,可以将文档分为不同的群体,并对不同群体的文档进行分类和管理。例如,可以对相似的文档进行聚类,以方便用户检索和浏览。此外......
  • 【VINKA原厂技术支持】电源供电系列高稳定性抗干扰VK36Q4 DFN10 封装更小厚度超薄的四
    1.概述VK36Q4具有4个触摸按键,可用来检测外部触摸按键上人手的触摸动作。该芯片具有较高的集成度,仅需极少的外部组件便可实现触摸按键的检测。提供了4路直接输出功能。芯片内部采用特殊的集成电路,具有高电源电压抑制比,可减少按键检测错误的发生,此特性保证在不利环境条件的应用......
  • JMeter-BeanShell预处理程序和BeanShell后置处理程序的应用
    一、什么是BeanShell?BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,JMeter性能测试工具也充分接纳了BeanShell解释器,封装成了可配置的BeanShell前置和后置处理器,分别是BeanShellPreprocessor(BeanShell预处理程序)和BeanShellPostprocessor......
  • 应用层
    C/S模型  P2P模型  DNS域名解析服务器1.递归查询:顶级域名->权限域名->本地域名2.迭代查询顶级域名<-权限域名<-本地域名 文件传输系统FTP:    HTTP协议万维网(WWW)以C/S方式工作,用户使用的浏览器就是万维网客户程序,万维网啊文档所驻留的主机运行服务器......
  • 电商平台商品详情接口的应用场景
     ☞ 商品接口的定义价格、库存量、发货地点等。此外,它还可以提供商品的详细信息,包括商品的图片、详细描述、规格参数、售后服务等。这些信息可以帮助用户更好地了解商品,从而更好地选择商品。其次,电商平台商品详情接口的实现原理是基于RESTfulAPI。RESTfulAPI是一种基于HT......
  • Docker 应用部署
    一、mysql部署在Docker容器中部署mysql,并通过外部mysql客户端操作MySQLserver容器内的网络服务和外部机器不能直接通信外部机器和宿主机可以直接通信宿主机和容器可以直接通信当容器中的网络服务需要被外部机器访问时,可以将容器中提供服务的端口映射到宿主机的端口上,外......
  • 华为快应用-在其他平台审核出现的白屏问题
    最近开发中,提交到oppo,vivo等市场的包在审核时出现了白屏问题,经过本地调试,并未发现有报错出现,百思不得其解,后来经过了解找到问题的所在。原因是:你开发的环境是基于华为快应用平台还是华为快应用联盟平台,这里简单解释下,因为快应用华为起到一个推进作用,所以华为平台的更新会更快,技术会......
  • 玖章算术CEO叶正盛在数据技术嘉年华分享NineData AIGC的应用实践
    4月8日下午,为期两天的第十二届数据技术嘉年华(DTC2023)在北京新云南皇冠假日酒店圆满落下帷幕。大会得到了工业和信息化部电子五所的支持和指导,围绕“开源·融合·数字化——引领数据技术发展,释放数据要素价值”这一主题,通过一场主论坛和十二场专题论坛,汇聚“产学研”各界数据技术......
  • LoRa温湿度传感器广泛应用
    LORA温湿度传感器是基于LORA无线传输的低功耗数据采集上传系列产品之一。模块选用进口高性能32位ARM主控器和高质量元器件,在高温、高湿、高寒等恶劣环境中具有可靠性好、极强的长期稳定性、抗干扰性强等优良性能。本产品可广泛应用于农业实验室,工业,环保,卫生防疫,仓储,温室,机房等领......