У меня есть приложение, в котором мне нужно удалить одно представление из стека UINavigationController и заменить его другим. Ситуация такова, что первое представление создает редактируемый элемент, а затем заменяет себя редактором этого элемента. Когда я делаю очевидное решение с первого взгляда:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];
У меня очень странное поведение. Обычно появляется окно редактора, но если я попытаюсь использовать кнопку «Назад» на панели навигации, я получаю дополнительные экраны, некоторые пустые, а некоторые просто испорченные. Название тоже становится случайным. Это похоже на то, что стек навигации полностью залит.
Как лучше подойти к этой проблеме?
Спасибо, Мэтт
источник
Следующий подход мне кажется более приятным, и он также хорошо работает с ARC:
UIViewController *newVC = [[UIViewController alloc] init]; // Replace the current view controller NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]]; [viewControllers removeLastObject]; [viewControllers addObject:newVC]; [[self navigationController] setViewControllers:viewControllers animated:YES];
источник
if(indexPath.row == 0){UIViewController *newVC = [[UIViewController alloc] init];newVC = [self.storyboard instantiateViewControllerWithIdentifier:@"Item1VC"]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[_detailViewController.navigationController viewControllers]]; [viewControllers removeLastObject];[viewControllers addObject:newVC]; [_detailViewController.navigationController setViewControllers:viewControllers animated:YES];}
По опыту, вам придется
viewControllers
напрямую возиться со свойством UINavigationController . Примерно так должно работать:MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; [[self retain] autorelease]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; self.navigationController.viewControllers = controllers; [self.navigationController pushViewController:mevc animated: YES];
Примечание: я изменил сохранение / освобождение на сохранение / автоматическое освобождение, поскольку это обычно более надежно - если между сохранением / выпуском возникает исключение, вы протекаете самостоятельно, но автозапуск позаботится об этом.
источник
После больших усилий (и корректировки кода Кевина) я наконец понял, как это сделать в контроллере представления, который извлекается из стека. Проблема, с которой я столкнулся, заключалась в том, что self.navigationController возвращал nil после того, как я удалил последний объект из массива контроллеров. Я думаю, это произошло из-за этой строки в документации для UIViewController в методе экземпляра navigationController «Возвращает контроллер навигации, только если контроллер представления находится в его стеке».
Я думаю, что как только текущий контроллер представления будет удален из стека, его метод navigationController вернет nil.
Вот исправленный код, который работает:
UINavigationController *navController = self.navigationController; MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; navController.viewControllers = controllers; [navController pushViewController:mevc animated: YES];
источник
Спасибо, это именно то, что мне нужно. Я также вставил это в анимацию, чтобы получить загиб страницы:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; UINavigationController *navController = self.navigationController; [[self retain] autorelease]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7]; [UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO]; [navController popViewControllerAnimated:NO]; [navController pushViewController:mevc animated:NO]; [UIView commitAnimations];
0.6 длительность - это быстро, хорошо для 3GS и новее, 0.8 все еще слишком быстро для 3G ..
Йохан
источник
Если вы хотите отобразить любой другой контроллер представления с помощью popToRootViewController, вам необходимо сделать следующее:
UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:[NSBundle mainBundle]]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]]; [viewControllers removeAllObjects]; [viewControllers addObject:newVC]; [[self navigationController] setViewControllers:viewControllers animated:NO];
Теперь весь ваш предыдущий стек будет удален, и новый стек будет создан с вашим требуемым rootViewController.
источник
Недавно мне пришлось сделать то же самое и основать свое решение на ответе Майклса. В моем случае мне пришлось удалить два контроллера представления из стека навигации, а затем добавить новый контроллер представления. Вызов
дважды работал нормально в моем случае.UINavigationController *navController = self.navigationController; // retain ourselves so that the controller will still exist once it's popped off [[self retain] autorelease]; searchViewController = [[SearchViewController alloc] init]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; // In my case I want to go up two, then push one.. [controllers removeLastObject]; navController.viewControllers = controllers; NSLog(@"controllers: %@",controllers); controllers = nil; [navController pushViewController:searchViewController animated: NO];
источник
Этот
UINavigationController
метод экземпляра может работать ...Всплывает контроллеры представления, пока указанный контроллер представления не станет контроллером вида сверху, а затем обновляет отображение.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
источник
Вот еще один подход, который не требует прямого вмешательства в массив viewControllers. Проверьте, был ли контроллер выдвинут, если да, нажмите на него.
TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil]; if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound) { [navigationController pushViewController:taskViewController animated:animated]; } else { [navigationController popToViewController:taskViewController animated:animated]; }
источник
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy]; for(int i=0;i<controllers.count;i++){ [controllers removeLastObject]; } self.navigationController.viewControllers = controllers;
источник
Мой любимый способ сделать это - использовать категорию в UINavigationController. Следующее должно работать:
UINavigationController + Helpers.h #import
@interface UINavigationController (Helpers) - (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller; @end
UINavigationController + Helpers.m
#import "UINavigationController + Helpers.h"
@implementation UINavigationController (Helpers) - (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller { UIViewController* topController = self.viewControllers.lastObject; [[topController retain] autorelease]; UIViewController* poppedViewController = [self popViewControllerAnimated:NO]; [self pushViewController:controller animated:NO]; return poppedViewController; } @end
Затем из вашего контроллера представления вы можете заменить вид сверху новым следующим образом:
[self.navigationController replaceTopViewControllerWithViewController: newController];
источник
Вы можете проверить с помощью массива контроллеров представления навигации, который вы даете вам все контроллеры представления, которые вы добавили в стек навигации. Используя этот массив, вы можете вернуться к определенному контроллеру представления.
источник
Для монотач / xamarin IOS:
внутри класса UISplitViewController;
UINavigationController mainNav = this._navController; //List<UIViewController> controllers = mainNav.ViewControllers.ToList(); mainNav.ViewControllers = new UIViewController[] { }; mainNav.PushViewController(detail, true);//to have the animation
источник
В качестве альтернативы,
Вы можете использовать,
category
чтобы неself.navigationController
бытьnil
послеpopViewControllerAnimated
просто нажмите и нажмите, это легко понять, доступ не требуется
viewControllers
....// UINavigationController+Helper.h @interface UINavigationController (Helper) - (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated; @end // UINavigationController+Helper.m @implementation UINavigationController (Helper) - (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated { UIViewController *v =[self popViewControllerAnimated:NO]; [self pushViewController:viewController animated:animated]; return v; } @end
В вашем ViewController
// #import "UINavigationController+Helper.h" // invoke in your code UIViewController *v= [[MyNewViewController alloc] init]; [self.navigationController popThenPushViewController:v animated:YES]; RELEASE_SAFELY(v);
источник
Не совсем ответ, но может помочь в некоторых сценариях (например, в моем):
Если вам нужно открыть контроллер просмотра C и перейти к B (вне стека) вместо A (ниже C), можно нажать B перед C и иметь все 3 в стеке. Сохраняя нажатие кнопки B невидимым и выбирая, использовать ли только C или C и B вместе, вы можете добиться того же эффекта.
начальная проблема A -> C (я хочу открыть C и показать B, вне стека)
возможное решение A -> B (нажатие невидимым) -> C (когда я нажимаю C, я выбираю показать B или также вытолкнуть его)
источник
Я использую это решение для сохранения анимации.
[self.navigationController pushViewController:controller animated:YES]; NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers]; [newControllers removeObject:newControllers[newControllers.count - 2]]; [self.navigationController setViewControllers:newControllers];
источник