iOS: Is there currently a way to prevent two view controllers being pushed or popped at the same time?

后端 未结 3 1191
萌比男神i
萌比男神i 2020-12-19 23:38

The only solution I have seen was an answer to a stackoverflow question. I posted the link below. The answer I am referring is the 5th one. It seems that some users have so

3条回答
  •  时光取名叫无心
    2020-12-20 00:08

    Inspired by @Lindsey Scott answer I created UINavigationController subclass. The advantage of my solution is that it also handles popping, and you can actually execute all requests after each other without problems(this is controlled via acceptConflictingCommands flag).

    MyNavigationController.h

    #import 
    
    @interface MyNavigationController : UINavigationController
    
    @property(nonatomic, assign) BOOL acceptConflictingCommands;
    
    @end
    

    MyNavigationController.m

    #import "MyNavigationController.h"
    
    @interface MyNavigationController ()
    
    @property(nonatomic, assign) BOOL shouldIgnoreStackRequests;
    @property(nonatomic, strong) NSMutableArray* waitingCommands;
    
    @end
    
    @implementation MyNavigationController
    
    -(instancetype)init
    {
        if( self = [super init] )
        {
            self.delegate = self;
            _waitingCommands = [NSMutableArray new];
        }
    
        return self;
    }
    
    -(instancetype)initWithRootViewController:(UIViewController *)rootViewController
    {
        if( self = [super initWithRootViewController:rootViewController] )
        {
            self.delegate = self;
            _waitingCommands = [NSMutableArray new];
            _acceptConflictingCommands = YES;
        }
    
        return self;
    }
    
    -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
    {
        if( !_shouldIgnoreStackRequests )
        {
            [super pushViewController:viewController animated:animated];
    
            _shouldIgnoreStackRequests = YES;
        }
        else if (_acceptConflictingCommands)
        {
            __weak typeof(self) weakSelf = self;
    
            //store and push it after current transition ends
            [_waitingCommands addObject:^{
    
                id strongSelf = weakSelf;
    
                [strongSelf pushViewController:viewController animated:animated];
    
            }];
        }
    
    }
    
    -(UIViewController *)popViewControllerAnimated:(BOOL)animated
    {
        __block UIViewController* popedController = nil;
    
        if( 1 < self.viewControllers.count )
        {
            if( !_shouldIgnoreStackRequests )
            {
                popedController = [super popViewControllerAnimated:animated];
    
                _shouldIgnoreStackRequests = YES;
            }
            else if( _acceptConflictingCommands )
            {
                __weak typeof(self) weakSelf = self;
    
                [_waitingCommands addObject:^{
    
                    id strongSelf = weakSelf;
    
                    popedController = [strongSelf popViewControllerAnimated:animated];
    
                }];
            }
        }
    
        return popedController;
    }
    
    #pragma mark - uinavigationcontroller delegate
    - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
    {
        _shouldIgnoreStackRequests = NO;
    
        if( 0 < _waitingCommands.count )
        {
            void(^waitingAction)() = _waitingCommands.lastObject;
            [_waitingCommands removeLastObject];
            waitingAction();
        }
    }
    
    @end
    

    Of course you can change default value of acceptConflictingCommands or control it externally.

    If your code happens to use popToRootViewController, setViewControllers:animated: and/or popToViewController you have to override them in the same manner to make sure they won't brake navigation stack.

提交回复
热议问题