Làm cách nào để bật một chế độ xem từ UINavigationController và thay thế nó bằng một chế độ xem khác trong một thao tác?


84

Tôi có một ứng dụng mà tôi cần xóa một chế độ xem khỏi ngăn xếp UINavigationController và thay thế nó bằng một chế độ xem khác. Tình huống là chế độ xem đầu tiên tạo một mục có thể chỉnh sửa và sau đó thay thế chính nó bằng một trình chỉnh sửa cho mục đó. Khi tôi thực hiện giải pháp rõ ràng trong chế độ xem đầu tiên:

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

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

Tôi nhận được hành vi rất kỳ lạ. Thông thường, chế độ xem trình chỉnh sửa sẽ xuất hiện, nhưng nếu tôi cố gắng sử dụng nút quay lại trên thanh điều hướng, tôi sẽ nhận được các màn hình phụ, một số trống và một số chỉ bị hỏng. Tiêu đề cũng trở nên ngẫu nhiên. Nó giống như ngăn xếp điều hướng hoàn toàn bị hosed.

Cách tiếp cận tốt hơn cho vấn đề này là gì?

Cảm ơn, Matt

Câu trả lời:


137

Tôi đã phát hiện ra rằng bạn không cần phải xử lý viewControllerstài sản theo cách thủ công . Về cơ bản có 2 điều khó khăn về điều này.

  1. self.navigationControllersẽ trở lại nilnếuself hiện không có trên ngăn xếp của bộ điều khiển điều hướng. Vì vậy, hãy lưu nó vào một biến cục bộ trước khi bạn mất quyền truy cập vào nó.
  2. Bạn phải retain(và đúng cách release) selfnếu không đối tượng sở hữu phương thức bạn đang sử dụng sẽ bị phân bổ, gây ra sự kỳ lạ.

Khi bạn đã chuẩn bị xong, chỉ cần bật và đẩy như bình thường. Mã này sẽ ngay lập tức thay thế bộ điều khiển hàng đầu bằng bộ điều khiển khác.

// 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];

Trong dòng cuối cùng đó nếu bạn thay đổi animatedthành YES, thì màn hình mới sẽ thực sự chuyển động và bộ điều khiển bạn vừa xuất hiện sẽ hoạt hình. Trông khá đẹp!


xuất sắc! giải pháp tốt hơn nhiều
emmby

Tuyệt vời. Mặc dù tôi không cần gọi [[tự giữ lại] autorelease], nhưng nó vẫn hoạt động tốt.
iamj4de

4
Có lẽ là một bổ sung hiển nhiên, nhưng sau đó bạn có thể đặt đoạn mã ở trên vào một khối hoạt ảnh để tạo hoạt ảnh cho quá trình chuyển đổi: [UIView beginAnimations: @ "View Flip" context: nil]; [UIView setAnimationDuration: 0,80]; [UIView setAnimationCurve: UIViewAnimationCurveEaseInOut]; [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView: navController.view cache: NO]; [navController pushViewController: newController animation: YES]; [UIView commitAnimations];
Martin

11
Hoạt động hiệu quả với ARC chỉ bằng cách loại bỏ dòng giữ lại / tự động khôi phục.
Ian Terrell

2
@TomerPeled Vâng, câu trả lời này đã gần 5 năm tuổi ... Tôi nghĩ đó là trường hợp của iOS 3. Các API đã thay đổi đủ để tôi không chắc đó là câu trả lời tốt nhất nữa.
Alex Wayne

56

Cách tiếp cận sau có vẻ đẹp hơn đối với tôi và cũng hoạt động tốt với 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];

1
@LukeRogers, điều này gây ra cảnh báo sau cho tôi: Đang kết thúc chuyển đổi điều hướng ở trạng thái không mong muốn. Cây xem phụ của Thanh điều hướng có thể bị hỏng. Có cách nào để ngăn chặn nó?
zaitsman

Sử dụng giải pháp này, bạn ghi đè cửa sổ bật lên. Và để hiển thị trong Chế độ xem chi tiết, mã của bạn nên đọc: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];}
LAOMUSIC ARTS

Những gì tôi đang tìm kiếm.
JERC

9

Theo kinh nghiệm, bạn sẽ phải viewControllerstrực tiếp tìm hiểu thuộc tính của UINavigationController . Một cái gì đó như thế này sẽ hoạt động:

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];

Lưu ý: Tôi đã thay đổi giữ lại / phát hành thành giữ lại / tự động khôi phục vì điều đó thường mạnh mẽ hơn - nếu một ngoại lệ xảy ra giữa giữ lại / phát hành, bạn sẽ tự bị rò rỉ, nhưng autorelease sẽ giải quyết vấn đề đó.


7

Sau nhiều nỗ lực (và chỉnh sửa mã từ Kevin), cuối cùng tôi đã tìm ra cách thực hiện điều này trong bộ điều khiển chế độ xem đang được bật ra từ ngăn xếp. Vấn đề mà tôi gặp phải là self.navigationController trả về con số không sau khi tôi xóa đối tượng cuối cùng khỏi mảng bộ điều khiển. Tôi nghĩ rằng đó là do dòng này trong tài liệu cho UIViewController trên phương thức thể hiện navigationController "Chỉ trả về bộ điều khiển điều hướng nếu bộ điều khiển chế độ xem nằm trong ngăn xếp của nó."

Tôi nghĩ rằng một khi bộ điều khiển chế độ xem hiện tại bị xóa khỏi ngăn xếp, phương thức navigationController của nó sẽ trả về nil.

Đây là mã điều chỉnh hoạt động:

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];

Điều này mang lại cho tôi một toàn bộ màu đen!
LAOMUSIC ARTS

4

Cảm ơn, đây chính xác là những gì tôi cần. Tôi cũng đặt cái này trong một hình ảnh động để cuộn trang:

        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];

Thời lượng 0,6 là nhanh, tốt cho 3GS và mới hơn, 0,8 vẫn còn hơi quá nhanh đối với 3G ..

Johan


Mã của bạn chính xác là những gì tôi đã sử dụng, tuyệt vời! Cảm ơn. Một lưu ý: với chuyển đổi cuộn trang, tôi có một tác phẩm nghệ thuật màu trắng ở dưới cùng của chế độ xem (ai biết tại sao) nhưng với việc lật nó hoạt động tốt. Dù sao, đây là mã đẹp và nhỏ gọn!
David H

3

Nếu bạn muốn hiển thị bất kỳ bộ điều khiển chế độ xem nào khác bằng popToRootViewController thì bạn cần làm như sau:

         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];

Bây giờ, tất cả ngăn xếp trước đó của bạn sẽ bị xóa và ngăn xếp mới sẽ được tạo với rootViewController yêu cầu của bạn.


1

Tôi đã phải làm một điều tương tự gần đây và dựa trên giải pháp của tôi dựa trên câu trả lời của Michaels. Trong trường hợp của tôi, tôi phải xóa hai Bộ điều khiển Chế độ xem khỏi Ngăn xếp Điều hướng và sau đó thêm Bộ điều khiển Chế độ xem mới vào. Kêu gọi

[bộ điều khiển removeLastObject];
hai lần, hoạt động tốt trong trường hợp của tôi.

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];


1

Đây UINavigationControllerphương pháp dụ có thể làm việc ...

Dừng bộ điều khiển chế độ xem cho đến khi bộ điều khiển chế độ xem được chỉ định là bộ điều khiển chế độ xem trên cùng và sau đó cập nhật màn hình.

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated

1

Đây là một cách tiếp cận khác không yêu cầu trực tiếp gây rối với mảng viewControllers. Kiểm tra xem bộ điều khiển đã được bật lên chưa, nếu có hãy đẩy nó.

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
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
    for(int i=0;i<controllers.count;i++){
       [controllers removeLastObject];
    }
 self.navigationController.viewControllers = controllers;

điều này gây ra cảnh báo cho tôi trong bảng điều khiển - Đang hoàn thành quá trình chuyển đổi điều hướng ở trạng thái không mong muốn. Cây xem phụ của Thanh điều hướng có thể bị hỏng. Có cách nào để ngăn chặn nó?
zaitsman

1

Cách yêu thích của tôi để làm điều đó là với một danh mục trên UINavigationController. Những điều sau sẽ hoạt động:

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

Sau đó, từ bộ điều khiển chế độ xem của bạn, bạn có thể thay thế chế độ xem trên cùng bằng chế độ xem mới bằng cách như sau:

[self.navigationController replaceTopViewControllerWithViewController: newController];

0

Bạn có thể kiểm tra với mảng bộ điều khiển chế độ xem điều hướng mà bạn cung cấp cho bạn tất cả các bộ điều khiển chế độ xem mà bạn đã thêm vào ngăn xếp điều hướng. Bằng cách sử dụng mảng đó, bạn có thể điều hướng trở lại bộ điều khiển chế độ xem cụ thể.


0

Đối với monotouch / xamarin IOS:

bên trong lớp UISplitViewController;

UINavigationController mainNav = this._navController; 
//List<UIViewController> controllers = mainNav.ViewControllers.ToList();
mainNav.ViewControllers = new UIViewController[] { }; 
mainNav.PushViewController(detail, true);//to have the animation

0

Ngoài ra,

Bạn có thể sử dụng categoryđể tránh self.navigationControllerphải nilsaupopViewControllerAnimated

chỉ cần bật và đẩy, rất dễ hiểu, không cần truy cập 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

Trong ViewController của bạn

// #import "UINavigationController+Helper.h"
// invoke in your code
UIViewController *v= [[MyNewViewController alloc] init];

[self.navigationController popThenPushViewController:v animated:YES];

RELEASE_SAFELY(v);

0

Không chính xác là câu trả lời nhưng có thể hữu ích trong một số trường hợp (ví dụ của tôi):

Nếu bạn cần bật bộ điều khiển chế độ xem C và đi đến B (ngoài ngăn xếp) thay vì A (một dưới C), bạn có thể đẩy B trước C và có cả 3 trên ngăn xếp. Bằng cách giữ cho nút B ẩn và bằng cách chọn chỉ bật C hoặc C và B hoàn toàn, bạn có thể đạt được hiệu quả tương tự.

vấn đề ban đầu A -> C (Tôi muốn bật C và hiển thị B, ra khỏi ngăn xếp)

giải pháp khả thi A -> B (được đẩy ẩn) -> C (khi tôi bật C, tôi chọn hiển thị B hoặc cũng bật nó)


0

Tôi sử dụng giải pháp này để giữ hình ảnh động.

[self.navigationController pushViewController:controller animated:YES];
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newControllers removeObject:newControllers[newControllers.count - 2]];
[self.navigationController setViewControllers:newControllers];
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.