iOS:目前是否有一种方法可以防止两个视图控制器同时被推送或弹出? [英] iOS: Is there currently a way to prevent two view controllers being pushed or popped at the same time?

查看:1372
本文介绍了iOS:目前是否有一种方法可以防止两个视图控制器同时被推送或弹出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我见过的唯一解决方案是一个stackoverflow问题的答案。我发布了下面的链接。我所说的答案是第五个。看来,一些用户有一些问题的解决方案。我不知道是否有另一个类别,以防止两个控制器被同时推。

  #importUINavigationController + Consistent.h
#import< objc / runtime.h>
///这个字符用于为isPushingViewController属性添加存储。
static char const * const ObjectTagKey =ObjectTag;

@interface UINavigationController()
@property(readwrite,getter = isViewTransitionInProgress)BOOL viewTransitionInProgress;

@end

@implementation UINavigationController(一致的)

- (void)setViewTransitionInProgress:(BOOL)property {
NSNumber * number = [NSNumber numberWithBool:property];
objc_setAssociatedObject(self,ObjectTagKey,number,OBJC_ASSOCIATION_RETAIN)
}


- (BOOL)isViewTransitionInProgress {
NSNumber * number = objc_getAssociatedObject(self,ObjectTagKey);

return [number boolValue];
}


#pragma mark - 拦截Pop,Push,PopToRootVC
/// @name拦截Pop,Push,PopToRootVC

- (NSArray *)safePopToRootViewControllerAnimated:(BOOL)animated {
if(self.viewTransitionInProgress)return nil;
if(动画){
self.viewTransitionInProgress = YES;
}
// - 这不是递归,由于方法swizzling调用下面的调用,调用原方法。
return [self safePopToRootViewControllerAnimated:animated];




- (NSArray *)safePopToViewController:(UIViewController *)viewController animated:(BOOL)animated {
if(self.viewTransitionInProgress) return nil;
if(animated){
self.viewTransitionInProgress = YES;
}
// - 这不是递归,由于方法swizzling调用下面的调用,调用原方法。
return [self safePopToViewController:viewController animated:animated]
}


- (UIViewController *)safePopViewControllerAnimated:(BOOL)animated {
if(self.viewTransitionInProgress)return nil;
if(animated){
self.viewTransitionInProgress = YES;
}
// - 这不是递归,由于方法swizzling调用下面的调用,调用原方法。
return [self safePopViewControllerAnimated:animated];
}



- (void)safePushViewController:(UIViewController *)viewController animated:(BOOL)animated {
self.delegate = self;
// - 如果我们已经推送一个视图控制器,我们不推另一个。
if(self.isViewTransitionInProgress == NO){
// - 这不是递归,由于方法swizzling调用下面的调用原来​​的方法。
[self safePushViewController:viewController animated:animated];
if(animated){
self.viewTransitionInProgress = YES;
}
}
}


//这被确认为App Store安全。
//如果使用私有API感到不舒服,还可以使用委托方法navigationController:didShowViewController:animated :.
- (void)safeDidShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// - 这不是递归。由于方法swizzling这是调用原方法。
[self safeDidShowViewController:viewController animated:animated];
self.viewTransitionInProgress = NO;
}


//如果用户没有完成滑动到后退手势,我们需要拦截它并将标志重新设置为NO。
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
id< UIViewControllerTransitionCoordinator> tc = navigationController.topViewController.transitionCoordinator;
[tc notifyWhenInteractionEndsUsingBlock:^(id< UIViewControllerTransitionCoordinatorContext> context){
self.viewTransitionInProgress = NO;
// - 重新启用向后滑动手势。
self.interactivePopGestureRecognizer.delegate =(id< UIGestureRecognizerDelegate>)viewController;
[self.interactivePopGestureRecognizer setEnabled:YES];
}];
// - 在委托的情况下,方法swizzling不工作所以:
// - 如果有一个不同于我们自己,转发这个方法到原始委托。
if(navigationController.delegate!= self){
[navigationController.delegate navigationController:navigationController
willShowViewController:viewController
animated:animated];
}
}


+(void)load {
// - 使用我们的自定义实例交换原始实现。
method_exchangeImplementations(class_getInstanceMethod(self,@selector(pushViewController:animated :)),class_getInstanceMethod(self,@selector(safePushViewController:animated :)));
method_exchangeImplementations(class_getInstanceMethod(self,@selector(didShowViewController:animated :)),class_getInstanceMethod(self,@selector(safeDidShowViewController:animated :)));
method_exchangeImplementations(class_getInstanceMethod(self,@selector(popViewControllerAnimated :)),class_getInstanceMethod(self,@selector(safePopViewControllerAnimated :)));
method_exchangeImplementations(class_getInstanceMethod(self,@selector(popToRootViewControllerAnimated :)),class_getInstanceMethod(self,@selector(safePopToRootViewControllerAnimated :)));
method_exchangeImplementations(class_getInstanceMethod(self,@selector(popToViewController:animated :)),class_getInstanceMethod(self,@selector(safePopToViewController:animated :)));
}

@end

iOS应用程式错误 - 无法将自己新增为子视图

解决方案

更新答案



c $ c> nonamelive 在Github上,我最初发布: https:// gist .github.com / nonamelive / 9334458 。通过继承 UINavigationController 并利用 UINavigationControllerDelegate 的优势,您可以确定转换发生的时间,防止其他转换发生在这个过渡期间,并且这样做所有在同一类。这里是nonamelive的解决方案的更新,排除了私人API:

  #importNavController.h

@interface NavController()

@property(nonatomic,assign)BOOL shouldIgnorePushingViewControllers;

@end

@implementation NavController

- (void)viewDidLoad {
[super viewDidLoad];
//在加载视图之后执行任何其他设置。
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
//处理可以重新创建的任何资源。
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if(!self.shouldIgnorePushingViewControllers)
{
[super pushViewController:viewController animated:animated];
}
self.shouldIgnorePushingViewControllers = YES;
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
self.shouldIgnorePushingViewControllers =
}

@end






上一个答案: isBeingPresented 上一个答案:


$ c>和 isBeingDismissed 仅适用于 viewDidLoad: viewDidApper:

虽然我自己没有测试过, b

由于您使用的是 UINavigationController ,您可以访问导航堆栈的内容,如下所示:

  NSArray * viewControllers = self.navigationController.viewControllers; 

通过视图控制器数组,如果需要,可以访问一些或所有相关的索引。 / p>

幸运的是,在iOS 5中引入了两个特别方便的方法: isBeingPresented isBeingDismissed 如果视图控制器处于提出或被解雇;



例如,这里有一种方法:

  NSArray * viewControllers = self.navigationController.viewControllers; 

for(UIViewController * viewController in viewControllers){

if(viewController.isBeingPresented || viewController.isBeingDismissed){
//在这种情况下, push已经在进行,不要执行
//弹出或推送当前视图控制器。也许在延迟后返回到这个
//方法再次检查这个条件。
return;
}
}

//否则,如果你让它通过循环不间断,执行push或pop当前视图控制器的
//。

实际上,你可能不必遍历堆栈上的每个视图控制器,这个建议将帮助你在右脚。


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 some problems with the solution however. I don't know if there is another category to prevent two controllers from being pushed at the same time. Any tips or suggestions are appreciated.

#import "UINavigationController+Consistent.h"
#import <objc/runtime.h>
/// This char is used to add storage for the isPushingViewController property.
 static char const * const ObjectTagKey = "ObjectTag";

 @interface UINavigationController ()
 @property (readwrite,getter = isViewTransitionInProgress) BOOL viewTransitionInProgress;

 @end

@implementation UINavigationController (Consistent)

- (void)setViewTransitionInProgress:(BOOL)property {
NSNumber *number = [NSNumber numberWithBool:property];
objc_setAssociatedObject(self, ObjectTagKey, number , OBJC_ASSOCIATION_RETAIN);
}


- (BOOL)isViewTransitionInProgress {
NSNumber *number = objc_getAssociatedObject(self, ObjectTagKey);

return [number boolValue];
}


 #pragma mark - Intercept Pop, Push, PopToRootVC
 /// @name Intercept Pop, Push, PopToRootVC

 - (NSArray *)safePopToRootViewControllerAnimated:(BOOL)animated {
if (self.viewTransitionInProgress) return nil;
if (animated) {
    self.viewTransitionInProgress = YES;
}
//-- This is not a recursion, due to method swizzling the call below calls the original  method.
return [self safePopToRootViewControllerAnimated:animated];

 }


  - (NSArray *)safePopToViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.viewTransitionInProgress) return nil;
if (animated) {
    self.viewTransitionInProgress = YES;
   }
//-- This is not a recursion, due to method swizzling the call below calls the original  method.
return [self safePopToViewController:viewController animated:animated];
   }


 - (UIViewController *)safePopViewControllerAnimated:(BOOL)animated {
if (self.viewTransitionInProgress) return nil;
if (animated) {
    self.viewTransitionInProgress = YES;
}
//-- This is not a recursion, due to method swizzling the call below calls the original  method.
return [self safePopViewControllerAnimated:animated];
 }



  - (void)safePushViewController:(UIViewController *)viewController animated:(BOOL)animated {
self.delegate = self;
//-- If we are already pushing a view controller, we dont push another one.
if (self.isViewTransitionInProgress == NO) {
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    [self safePushViewController:viewController animated:animated];
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    }
    }


// This is confirmed to be App Store safe.
// If you feel uncomfortable to use Private API, you could also use the delegate method navigationController:didShowViewController:animated:.
- (void)safeDidShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
//-- This is not a recursion. Due to method swizzling this is calling the original method.
[self safeDidShowViewController:viewController animated:animated];
self.viewTransitionInProgress = NO;
 }


// If the user doesnt complete the swipe-to-go-back gesture, we need to intercept it and set the flag to NO again.
 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
id<UIViewControllerTransitionCoordinator> tc = navigationController.topViewController.transitionCoordinator;
[tc notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    self.viewTransitionInProgress = NO;
    //--Reenable swipe back gesture.
    self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController;
    [self.interactivePopGestureRecognizer setEnabled:YES];
}];
//-- Method swizzling wont work in the case of a delegate so:
//-- forward this method to the original delegate if there is one different than ourselves.
if (navigationController.delegate != self) {
    [navigationController.delegate navigationController:navigationController
                                 willShowViewController:viewController
                                               animated:animated];
}
}


  + (void)load {
//-- Exchange the original implementation with our custom one.
method_exchangeImplementations(class_getInstanceMethod(self,  @selector(pushViewController:animated:)), class_getInstanceMethod(self, @selector(safePushViewController:animated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(didShowViewController:animated:)), class_getInstanceMethod(self, @selector(safeDidShowViewController:animated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(popViewControllerAnimated:)), class_getInstanceMethod(self, @selector(safePopViewControllerAnimated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToRootViewControllerAnimated:)), class_getInstanceMethod(self, @selector(safePopToRootViewControllerAnimated:)));
method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToViewController:animated:)), class_getInstanceMethod(self, @selector(safePopToViewController:animated:)));
 }

 @end

iOS app error - Can't add self as subview

解决方案

Updated answer:

I prefer this solution by nonamelive on Github to what I originally posted: https://gist.github.com/nonamelive/9334458. By subclassing the UINavigationController and taking advantage of the UINavigationControllerDelegate, you can establish when a transition is happening, prevent other transitions from happening during that transition, and do so all within the same class. Here's an update of nonamelive's solution which excludes the private API:

#import "NavController.h"

@interface NavController ()

@property (nonatomic, assign) BOOL shouldIgnorePushingViewControllers;

@end

@implementation NavController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (!self.shouldIgnorePushingViewControllers)
    {
        [super pushViewController:viewController animated:animated];
    }
    self.shouldIgnorePushingViewControllers = YES;
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    self.shouldIgnorePushingViewControllers = NO;
}

@end


Previous answer:

Problem with this Previous Answer: isBeingPresented and isBeingDismissed only work in viewDidLoad: or viewDidApper:

Although I haven't tested this myself, here is a suggestion.

Since you're using a UINavigationController, you can access the contents of your navigation stack, like so:

NSArray *viewControllers = self.navigationController.viewControllers;

And through that array of view controllers, you can access some or all relevant indices if need be.

Luckily, two especially convenient methods were introduced in iOS 5: isBeingPresented and isBeingDismissed which return "YES" if the view controller is in the process of being presented or being dismissed, respectively; "NO" otherwise.

So, for example, here's one approach:

NSArray *viewControllers = self.navigationController.viewControllers;

for (UIViewController *viewController in viewControllers) {

    if (viewController.isBeingPresented || viewController.isBeingDismissed) {
        // In this case when a pop or push is already in progress, don't perform
        // a pop or push on the current view controller. Perhaps return to this
        // method after a delay to check this conditional again.
        return;
    }
}

// Else if you make it through the loop uninterrupted, perform push or pop
// of the current view controller.

In actuality, you probably won't have to loop through every view controller on the stack, but perhaps this suggestion will help set you off on the right foot.

这篇关于iOS:目前是否有一种方法可以防止两个视图控制器同时被推送或弹出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆