我见过的唯一解决方案是回答stackoverflow问题。 我发布了以下链接。 我所指的答案是第五个。 但是,似乎有些用户在使用该解决方案时遇到了一些问题。 我不知道是否有另一个类别可以防止同时推送两个控制器。 任何提示或建议表示赞赏。
#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:))); } @endiOS应用程序错误 - 无法将self添加为子视图
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:))); } @endiOS app error - Can't add self as subview
最满意答案
更新的答案:
我更喜欢Github上的nonamelive解决方案,我最初发布的内容: https : nonamelive 。 通过UINavigationController并利用UINavigationControllerDelegate ,您可以确定转换何时发生,防止在转换期间发生其他转换,并在同一个类中完成所有转换。 这是nonamelive解决方案的更新,它排除了私有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上一个答案:
这个问题以前的答案: isBeingPresented和isBeingDismissed仅适用于viewDidLoad:或viewDidApper:
虽然我自己没有测试过,但这是一个建议。
由于您使用的是UINavigationController ,因此可以访问导航堆栈的内容,如下所示:
NSArray *viewControllers = self.navigationController.viewControllers;通过该视图控制器数组,您可以根据需要访问部分或全部相关索引。
幸运的是,iOS 5中引入了两种特别方便的方法: isBeingPresented和isBeingDismissed ,如果视图控制器分别处于呈现或被解除的过程中,则返回“YES”; 否则“不”。
所以,例如,这是一种方法:
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.实际上,您可能不必遍历堆栈中的每个视图控制器,但是这个建议可能会帮助您摆脱困境。
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; } @endPrevious 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.
更多推荐
发布评论