ReactNative iOS多个Modal冲突问题

2021-12-06  本文已影响0人  li_礼光

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

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

上一篇下一篇

猜你喜欢

热点阅读