Как я могу поместить представление из UINavigationController и заменить его другим за одну операцию?
У меня есть приложение, где мне нужно удалить одно представление из стека UINavigationController и заменить его на другое. Ситуация такова, что первое представление создает редактируемый элемент, а затем заменяет собой редактор для элемента. Когда я делаю очевидное решение в первом представлении:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];
Я получаю очень странное поведение. Обычно появится представление редактора, но если я попытаюсь использовать кнопку "Назад" на панели навигации, я получаю дополнительные экраны, некоторые пробелы, а некоторые просто прищурены. Название становится случайным. Это похоже на то, что стек nav полностью закрыт.
Что было бы лучшим подходом к этой проблеме?
Спасибо,
Matt
Ответы
Ответ 1
Я обнаружил, что вам не нужно вручную связываться с свойством viewControllers
. В принципе, есть две сложные вещи.
-
self.navigationController
вернет nil
, если self
в настоящее время не находится в стеке контроллера навигации. Поэтому сохраните его до локальной переменной, прежде чем вы потеряете доступ к ней.
- Вы должны
retain
(и правильно release
) self
или объект, которому принадлежит метод, в котором вы находитесь, будет освобожден, вызывая странность.
Как только вы сделаете эту подготовку, просто нажмите и нажмите, как обычно. Этот код мгновенно заменит верхний контроллер другим.
// locally store the navigation controller since
// self.navigationController will be nil once we are popped
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it popped off
[[self retain] autorelease];
// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];
В этой последней строке, если вы измените animated
на YES
, тогда новый экран будет на самом деле оживлен, и только что вы нажали контроллер. Выглядит неплохо!
Ответ 2
Следующий подход кажется мне более приятным, а также хорошо работает с 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];
Ответ 3
По опыту вам нужно будет напрямую играть с свойством UINavigationController viewControllers
. Что-то вроде этого должно работать:
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];
Примечание. Я изменил сохранение/освобождение на сохранение/авторекламу, поскольку это обычно более устойчиво - если возникает исключение между сохранением/выпуском, которое вы будете утечка себя, но autorelease позаботится об этом.
Ответ 4
После долгих усилий (и настройки кода от Кевина) я, наконец, понял, как это сделать в контроллере представления, который выталкивается из стека. Проблема, с которой я столкнулась, заключалась в том, что self.navigationController возвращал нуль после того, как я удалил последний объект из массива контроллеров. Я думаю, что это связано с этой линией в документации для
UIViewController для метода экземпляра navigationController
"Только возвращает контроллер навигации, если контроллер представления находится в стеке".
Я думаю, что как только текущий контроллер представления будет удален из стека, его метод navigationController вернет нуль.
Вот отредактированный код, который работает:
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];
Ответ 5
Спасибо, это было именно то, что мне нужно. Я также помещал это в анимацию, чтобы получить завиток страницы:
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.
Йохан
Ответ 6
Если вы хотите показать любой другой контроллер вида 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.
Ответ 7
Этот метод экземпляра UINavigationController
может работать...
Контролирует контроллеры просмотра до тех пор, пока указанный контроллер представления не будет контроллером верхнего уровня, а затем обновит отображение.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
Ответ 8
В последнее время мне пришлось сделать что-то похожее и основали мое решение на ответе Майклса. В моем случае мне пришлось удалить два контроллера просмотра из стека навигации, а затем добавить новый контроллер просмотра. Вызов
[controllers removeLastObject];
дважды, отлично работал в моем случае.
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it 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];
код >
Ответ 9
Вот еще один подход, который не требует прямого взаимодействия с массивом 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];
}
Ответ 10
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
for(int i=0;i<controllers.count;i++){
[controllers removeLastObject];
}
self.navigationController.viewControllers = controllers;
Ответ 11
Мой любимый способ сделать это с категорией в 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];
Ответ 12
Вы можете проверить с помощью массива контроллеров навигационных представлений, которые вы дадите всем контроллерам представлений, которые вы добавили в стек навигации. Используя этот массив, вы можете вернуться к определенному контроллеру вида.
Ответ 13
Для monotouch/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
Ответ 14
В качестве альтернативы,
Вы можете использовать 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);
Ответ 15
Не совсем ответ, но может помочь в некоторых сценариях (например, мой):
Если вам нужен pop viewcontroller C и перейти к B (из стека) вместо A (один ниже C), можно нажать B до C и иметь все 3 в стеке. Сохраняя невидимый B-эффект, и, выбирая только то, чтобы поместить только C или C и B, вы можете добиться того же эффекта.
начальная проблема
A → C (я хочу поместить C и показать B, из стека)
возможное решение
A → B (нажата невидимая) → C (когда я pop C, я выбираю, чтобы показать B или также поп его)
Ответ 16
Я использую это решение для сохранения анимации.
[self.navigationController pushViewController:controller animated:YES];
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newControllers removeObject:newControllers[newControllers.count - 2]];
[self.navigationController setViewControllers:newControllers];