美文网首页
ReactNative iOS多个Modal冲突问题

ReactNative iOS多个Modal冲突问题

作者: li_礼光 | 来源:发表于2021-12-06 15:53 被阅读0次

    RN的modal在iOS的系统中多次调用, 会导致RCTModalHostView不能正确的移除.

    系统报错
    2021-12-06 15:51:35.013675+0800 AqaraHome[1499:355873] [Presentation] Attempt to present <RCTModalHostViewController: 0x111013d50> on <LHTabbarViewController: 0x10c844600> (from <LHRNRootViewController: 0x10c9d4c00>) which is already presenting <RCTModalHostViewController: 0x149b35a50>.

    Modal冲突

    利用Hook方式解决

    简单的思路, 创建一个modal专用队列, 循序执行.

    LHRNModalHookManager.h
    //
    //  LHRNModalHookManager.h
    //  AqaraHome
    //
    //  Created by 礼光 on 2021/12/6.
    //  Copyright © 2021 Lumi United Technology Co., Ltd. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    @interface LHRNModalHookManager : NSObject
    
    @end
    NS_ASSUME_NONNULL_END
    
    LHRNModalHookManager.m
    //
    //  LHRNModalHookManager.m
    //  AqaraHome
    //
    //  Created by 礼光 on 2021/12/6.
    //  Copyright © 2021 Lumi United Technology Co., Ltd. All rights reserved.
    //
    
    #import "LHRNModalHookManager.h"
    #import <objc/message.h>
    
    @interface LHRNModalHookManager()
    @property (nonatomic, strong) NSOperationQueue *modelQueue;
    @property (nonatomic, strong) NSMutableArray <UIViewController *> *presentVCs;
    @property (nonatomic, assign) bool isModal;
    @end
    
    @implementation LHRNModalHookManager
    
    + (void)load {
        Method presentOldMethod = class_getInstanceMethod(NSClassFromString(@"RCTModalHostViewManager"), @selector(presentModalHostView:withViewController:animated:));
        Method presentNewMethod = class_getInstanceMethod(NSClassFromString(@"LHRNModalHookManager"), @selector(presentHookModalHostView:withViewController:animated:));
        method_exchangeImplementations(presentOldMethod, presentNewMethod);
        
        Method dismissOldMethod = class_getInstanceMethod(NSClassFromString(@"RCTModalHostViewManager"), @selector(dismissModalHostView:withViewController:animated:));
        Method dismissNewMethod = class_getInstanceMethod(NSClassFromString(@"LHRNModalHookManager"), @selector(dismissHookModalHostView:withViewController:animated:));
        method_exchangeImplementations(dismissOldMethod, dismissNewMethod);
    }
    
    - (void)presentHookModalHostView:(id)modalHostView
                  withViewController:(id)viewController
                            animated:(BOOL)animated {
        dispatch_block_t completionBlock = ^{ };
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:[LHRNModalHookManager shareInstance]
                                                                         selector:@selector(operationPresentViewController:)
                                                                           object:@[viewController, completionBlock]];
        [[LHRNModalHookManager shareInstance].presentVCs addObject:viewController];
        [[LHRNModalHookManager shareInstance].modelQueue addOperation:op];
    }
    
    - (void)dismissHookModalHostView:(id)modalHostView
                  withViewController:(id)viewController
                            animated:(BOOL)animated
    {
        UIViewController *vc = (UIViewController *)[LHRNModalHookManager shareInstance].presentVCs.firstObject;
        [vc dismissViewControllerAnimated:NO completion:nil];
        [[LHRNModalHookManager shareInstance].presentVCs removeObject:vc];
        [[LHRNModalHookManager shareInstance].modelQueue setSuspended:NO];
        if ([LHRNModalHookManager shareInstance].presentVCs.count == 0) {
            [LHRNModalHookManager shareInstance].isModal = NO;
        }
    }
    
    
    
    + (instancetype)shareInstance {
        static LHRNModalHookManager * manager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            manager = [[LHRNModalHookManager alloc] init];
            manager.modelQueue = [[NSOperationQueue alloc] init];
            manager.presentVCs = [NSMutableArray array];
        });
        return manager;
    }
    
    - (void)presentViewController:(UIViewController *)viewControllerToPresent
                       completion:(void (^ )(void))completion {
        
        id comp = completion;
        if (comp == nil) {
            comp = ^(){};
        }
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:[LHRNModalHookManager shareInstance]
                                                                         selector:@selector(operationPresentViewController:)
                                                                           object:@[viewControllerToPresent, comp]];
        [[LHRNModalHookManager shareInstance].modelQueue addOperation:op];
    }
    
    - (void)operationPresentViewController:(id)args {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSArray *params = (NSArray *)args;
            UIViewController *presentVC = params[0];
            id completion = params[1];
            UIWindow *keyWin = (UIWindow *)[[[UIApplication sharedApplication] windows] objectAtIndex:0];
            presentVC.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2];
            presentVC.modalPresentationStyle = UIModalPresentationFullScreen | UIModalPresentationOverCurrentContext;
            presentVC.hidesBottomBarWhenPushed = false;
            [keyWin.rootViewController presentViewController:presentVC animated:NO completion:completion];
            [[LHRNModalHookManager shareInstance].modelQueue setSuspended:YES];
            [LHRNModalHookManager shareInstance].isModal = YES;
        });
    }
    
    @end
    

    需要考虑的是, 兼容性的问题.

    相关文章

      网友评论

          本文标题:ReactNative iOS多个Modal冲突问题

          本文链接:https://www.haomeiwen.com/subject/gxtnxrtx.html