本文介绍: 如果不用 UIApplication 作为默认principalClassName,而是传入它的子类,一般是为了解决产品应用层面的管理问题例如设计一个 UIApplication子类叫。

简介

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,argvprincipalClassName 和 delegateClassName 。

argcargv需要介绍,是 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 一样,不需要过多赘述。

移动开发者联盟加入指引

原文地址:https://blog.csdn.net/madaxin/article/details/130112084

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_43444.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注