Как я могу извлечь представление из UINavigationController и заменить его другим в одной операции? - PullRequest
83 голосов
/ 04 января 2009

У меня есть приложение, в котором мне нужно удалить одно представление из стека UINavigationController и заменить его другим. Ситуация такова, что первое представление создает редактируемый элемент, а затем заменяет себя редактором для элемента. Когда я делаю очевидное решение в первом представлении:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];

У меня очень странное поведение. Обычно отображается окно редактора, но если я пытаюсь использовать кнопку «Назад» на навигационной панели, я получаю дополнительные экраны, некоторые пустые, а некоторые просто облажались. Название тоже становится случайным. Это похоже на то, что стек навигации полностью закрыт.

Как лучше подходить к этой проблеме?

Спасибо, Matt

Ответы [ 16 ]

137 голосов
/ 24 ноября 2009

Я обнаружил, что вам вообще не нужно вручную связываться со свойством viewControllers. В этом есть две хитрости.

  1. self.navigationController вернет nil, если self в данный момент отсутствует в стеке контроллера навигации. Поэтому сохраните его в локальной переменной, прежде чем потерять к нему доступ.
  2. Вы должны 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's popped off
[[self retain] autorelease];

// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];

В этой последней строке, если вы измените animated на YES, тогда новый экран будет фактически оживлен, а контроллер, который вы только что подключили, оживит. Выглядит довольно мило!

56 голосов
/ 04 января 2012

Следующий подход кажется мне более приятным, а также хорошо работает с 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];
9 голосов
/ 05 января 2009

Исходя из опыта, вам придется напрямую манипулировать свойством 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];

Примечание: я изменил сохранение / освобождение на сохранение / автоматическое освобождение, так как оно обычно более надежное - если между сохранением / освобождением произойдет исключение, вы утечете себя, но автоматическое освобождение позаботится об этом.

7 голосов
/ 10 января 2009

После долгих усилий (и настройки кода от Кевина) я наконец понял, как это сделать в контроллере представления, который извлекается из стека. У меня была проблема с тем, что self.navigationController возвращал ноль после того, как я удалил последний объект из массива контроллеров. Я думаю, что это было связано с этой строкой в ​​документации для 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];
4 голосов
/ 23 июня 2010

Спасибо, это было именно то, что мне было нужно. Я также поместил это в анимацию, чтобы получить завиток страницы:

        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

Юхан

3 голосов
/ 08 января 2014

Если вы хотите показать любой другой контроллер представления с помощью 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.

1 голос
/ 23 апреля 2013

Мой любимый способ сделать это с помощью категории на 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];
1 голос
/ 23 ноября 2012
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
    for(int i=0;i<controllers.count;i++){
       [controllers removeLastObject];
    }
 self.navigationController.viewControllers = controllers;
1 голос
/ 01 июня 2012

Вот еще один подход, который не требует непосредственной работы с массивом 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];
}
1 голос
/ 24 марта 2011

Недавно мне пришлось сделать нечто подобное, и мое решение основывалось на ответе Майклза. В моем случае мне пришлось удалить два контроллера просмотра из стека навигации, а затем добавить новый контроллер просмотра. Вызов

[controllers removeLastObject];
дважды, в моем случае работал нормально.

</p> <pre><code>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];

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...