Thay đổi animate bộ điều khiển xem mà không sử dụng ngăn xếp điều khiển điều hướng, xem trước hoặc bộ điều khiển phương thức?


142

NavigationControllers có các ngăn xếp ViewContoder để quản lý và hạn chế chuyển đổi hình ảnh động.

Việc thêm trình điều khiển chế độ xem dưới dạng chế độ xem phụ vào trình điều khiển chế độ xem hiện tại yêu cầu chuyển các sự kiện đến trình điều khiển chế độ xem phụ, điều này gây khó khăn cho việc quản lý, gây ra một chút phiền toái và nói chung cảm giác như bị hack khi thực hiện (Apple cũng khuyến nghị chống lại làm điều này).

Việc trình bày bộ điều khiển chế độ xem một lần nữa đặt bộ điều khiển chế độ xem lên trên bộ điều khiển khác và trong khi nó không có sự kiện vượt qua các sự cố được mô tả ở trên, thì nó không thực sự 'hoán đổi' bộ điều khiển chế độ xem, nó xếp chồng nó.

Bảng phân cảnh được giới hạn trong iOS 5 và gần như lý tưởng, nhưng không thể được sử dụng trong tất cả các dự án.

Ai đó có thể trình bày một VÍ DỤ MÃ RẮN trên một cách để thay đổi các bộ điều khiển xem mà không có các giới hạn ở trên và cho phép chuyển đổi hoạt hình giữa chúng không?

Một ví dụ gần gũi, nhưng không có hình ảnh động: Cách sử dụng nhiều bộ điều khiển xem tùy chỉnh iOS mà không cần bộ điều khiển điều hướng

Chỉnh sửa: Sử dụng Bộ điều khiển Nav là tốt, nhưng cần có các kiểu chuyển tiếp hoạt hình (không chỉ đơn giản là hiệu ứng trượt) mà bộ điều khiển xem đang hiển thị cần được hoán đổi hoàn toàn (không được xếp chồng lên nhau). Nếu bộ điều khiển khung nhìn thứ hai phải loại bỏ một bộ điều khiển khung nhìn khác khỏi ngăn xếp, thì nó không được gói gọn.

Chỉnh sửa 2: iOS 4 phải là hệ điều hành cơ bản cho câu hỏi này, tôi nên làm rõ rằng khi đề cập đến bảng phân cảnh (ở trên).


1
Bạn có thể thực hiện chuyển đổi hình ảnh động tùy chỉnh với bộ điều khiển điều hướng. Nếu điều này có thể chấp nhận được, vui lòng xóa ràng buộc đó khỏi câu hỏi của bạn và tôi sẽ đăng một ví dụ mã.
Richard Brightwell

@Richard nếu nó bỏ qua rắc rối trong việc quản lý ngăn xếp và điều chỉnh các kiểu chuyển đổi hoạt hình khác nhau giữa các bộ điều khiển xem thì sử dụng bộ điều khiển điều hướng là ổn!
TigerCoding

Được rồi, tốt lắm. Tôi đã mất kiên nhẫn và đăng mã. Hãy thử một lần. Làm việc cho tôi.
Richard Brightwell

@RichardBrightwell bạn đã nói ở đây rằng người ta có thể thực hiện chuyển đổi hoạt hình tùy chỉnh giữa các bộ điều khiển xem bằng bộ điều khiển điều hướng ... bằng cách nào? Bạn có thể gửi một ví dụ? cảm ơn.
Vịt

Câu trả lời:


108

EDIT: Câu trả lời mới hoạt động theo bất kỳ định hướng nào. Câu trả lời ban đầu chỉ hoạt động khi giao diện ở hướng dọc. Đây là hình ảnh chuyển tiếp của chế độ xem b / c thay thế một chế độ xem w / một chế độ xem khác phải xảy ra với các chế độ xem ít nhất là một mức dưới chế độ xem đầu tiên được thêm vào cửa sổ (ví dụwindow.rootViewController.view.anotherView).

Tôi đã triển khai một lớp container đơn giản mà tôi đã gọi TransitionController. Bạn có thể tìm thấy nó tại https://gist.github.com/1394947 .

Bên cạnh đó, tôi thích việc triển khai trong một lớp riêng biệt hơn để sử dụng lại dễ dàng hơn. Nếu bạn không muốn điều đó, bạn có thể chỉ cần thực hiện cùng một logic trực tiếp trong đại biểu ứng dụng của bạn để loại bỏ sự cần thiết của TransitionControllerlớp. Logic bạn cần sẽ giống nhau tuy nhiên.

Sử dụng nó như sau:

Trong đại biểu ứng dụng của bạn

// add a property for the TransitionController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    MyViewController *vc = [[MyViewContoller alloc] init...];
    self.transitionController = [[TransitionController alloc] initWithViewController:vc];
    self.window.rootViewController = self.transitionController;
    [self.window makeKeyAndVisible];
    return YES;
}

Để chuyển sang bộ điều khiển xem mới từ bất kỳ bộ điều khiển xem nào

- (IBAction)flipToView
{
    anotherViewController *vc = [[AnotherViewController alloc] init...];
    MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate.transitionController transitionToViewController:vc withOptions:UIViewAnimationOptionTransitionFlipFromRight];
}

EDIT: Câu trả lời gốc bên dưới - chỉ hoạt động cho định hướng chân dung

Tôi đã đưa ra các giả định sau đây cho ví dụ này:

  1. Bạn có một bộ điều khiển xem được gán làm rootViewControllercửa sổ của bạn

  2. Khi bạn chuyển sang chế độ xem mới, bạn muốn thay thế chế độ xem hiện tại bằng chế độ xem Bộ điều khiển sở hữu chế độ xem mới. Bất cứ lúc nào, chỉ có viewContoder hiện tại còn sống (ví dụ: alloc'ed).

Mã có thể dễ dàng sửa đổi để hoạt động khác nhau, điểm quan trọng là chuyển đổi hoạt hình và bộ điều khiển xem đơn. Hãy chắc chắn rằng bạn không giữ lại bộ điều khiển xem ở bất cứ đâu ngoài việc gán nó cho window.rootViewController.

Mã để chuyển đổi hoạt hình trong đại biểu ứng dụng

- (void)transitionToViewController:(UIViewController *)viewController
                    withTransition:(UIViewAnimationOptions)transition
{
    [UIView transitionFromView:self.window.rootViewController.view
                        toView:viewController.view
                      duration:0.65f
                       options:transition
                    completion:^(BOOL finished){
                        self.window.rootViewController = viewController;
                    }];
}

Ví dụ sử dụng trong bộ điều khiển xem

- (IBAction)flipToNextView
{
    AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
    MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate transitionToViewController:anotherVC
                             withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}

1
Vâng, rất tốt đẹp! Nó không chỉ thực hiện thủ thuật mà còn là một ví dụ mã rất đơn giản và rõ ràng. Cảm ơn nhiều!
TigerCoding

Không phải anh ta gây ra vấn đề trong cảnh quan cho bạn? Ngoài ra: điều này có kích hoạt các phương thức willAppear và didAppear của viewControllers không?
Felix Lamouroux

1
Tôi biết các cuộc gọi xuất hiện đang được thực hiện bởi vì tôi đã đăng nhập chúng. Tôi cũng không thấy lý do tại sao điều này sẽ ảnh hưởng đến thay đổi định hướng. Bạn có thể giải thích lý do tại sao bạn nghĩ rằng nó sẽ?
TigerCoding

1
@XJones là giải pháp tuyệt vời, hoạt động khá tốt trong ứng dụng iPad của tôi ngoại trừ một vấn đề tôi gặp phải, sau lần khởi tạo đầu tiên transVC = [TransitionContoder ... initWithViewControll: aUINavCtrlObj]; cửa sổ.rootVC = transVC; Chế độ xem trong aUINavCtrlObj được đệm 20px từ trên xuống (tức là 20 px dưới cùng nằm ngoài màn hình (UIWindow) trong tất cả các hướng), nhưng sau khi tôi thực hiện [transVC transToViewControll: otherVC] thì phần đệm đã biến mất. Tôi đã thử WantFullScreenLayout = NO trong loadView của TransitionContoder, những gì nó làm là thêm một vùng đen 20 px ngay dưới statusBar.
Abduliam Rehmanius

1
@AbduliamRehmanius: Tôi gặp vấn đề tương tự. Tôi đã sửa nó bằng cách thay đổi dòng 25 TransitionController.mthành UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];, nhưng tôi chỉ sử dụng cái này trên phiên bản iOS mới nhất, vì vậy hãy kiểm tra cẩn thận.
Phil Calvin

67

Bạn có thể sử dụng hệ thống ngăn chặn viewContoder mới của Apple. Để biết thêm thông tin chi tiết, hãy xem video phiên WWDC 2011 "Thực hiện UIViewControllerngăn chặn".

Mới đối với iOS5, UIViewControllerContainerment cho phép bạn có một viewContoder cha và một số viewControllers con được chứa trong nó. Đây là cách UISplitViewControll hoạt động. Làm điều này, bạn có thể xếp chồng các bộ điều khiển xem trong một cha mẹ, nhưng đối với ứng dụng cụ thể của bạn, bạn chỉ cần sử dụng cha mẹ để quản lý quá trình chuyển đổi từ một viewControll hiển thị này sang một cái khác. Đây là cách thức được Apple chấp thuận để làm mọi thứ và hoạt hình từ một viewControll con không gây đau đớn. Thêm vào đó bạn có thể sử dụng tất cả các UIViewAnimationOptionchuyển tiếp khác nhau !

Ngoài ra, với UIViewContainment, bạn không phải lo lắng, trừ khi bạn muốn, về sự lộn xộn của việc quản lý viewControllers con trong các sự kiện định hướng. Bạn có thể chỉ cần sử dụng các thao tác sau để đảm bảo cha mẹ của bạn chuyển tiếp các sự kiện xoay vòng tới viewViewrollers con.

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

Bạn có thể thực hiện các thao tác sau hoặc tương tự trong phương thức viewDidLoad của cha mẹ để thiết lập childViewControll đầu tiên:

[self addChildViewController:self.currentViewController];
[self.view addSubview:self.currentViewController.view];
[self.currentViewController didMoveToParentViewController:self];
[self.currentViewController.swapViewControllerButton setTitle:@"Swap" forState:UIControlStateNormal];

sau đó khi bạn cần thay đổi khung nhìn con, bạn gọi một cái gì đó dọc theo các dòng sau trong khung nhìn cha mẹ:

-(void)swapViewControllers:(childViewController *)addChildViewController:aNewViewController{
     [self addChildViewController:aNewViewController];
     __weak __block ViewController *weakSelf=self;
     [self transitionFromViewController:self.currentViewController
                       toViewController:aNewViewController
                               duration:1.0
                                options:UIViewAnimationOptionTransitionCurlUp
                             animations:nil
                             completion:^(BOOL finished) {
                                   [aNewViewController didMoveToParentViewController:weakSelf];

                                   [weakSelf.currentViewController willMoveToParentViewController:nil];
                                   [weakSelf.currentViewController removeFromParentViewController];

                                   weakSelf.currentViewController=[aNewViewController autorelease];
                             }];
 }

Tôi đã đăng một dự án ví dụ đầy đủ ở đây: https://github.com/toolmanGitHub/stackedViewControllers . Dự án khác này cho thấy cách sử dụng UIViewControllerNgăn chặn trên một số loại viewContaptor đầu vào khác nhau không chiếm toàn bộ màn hình. Chúc may mắn


1
Một ví dụ tuyệt vời. Cảm ơn bạn đã dành thời gian để gửi mã. Mặt khác, nó chỉ giới hạn ở iOS 5. Như đã đề cập trong câu hỏi: "[Bảng phân cảnh] được giới hạn ở iOS 5 và gần như lý tưởng, nhưng không thể được sử dụng trong tất cả các dự án." Xem xét một tỷ lệ lớn (khoảng 40%?) Khách hàng vẫn sử dụng iOS 4, mục tiêu là cung cấp một cái gì đó hoạt động trong iOS 4 trở lên.
TigerCoding

Bạn không nên gọi [self.currentViewController willMoveToParentViewController:nil];trước khi chuyển đổi?
Felix Lamouroux

@FelixLam - theo các tài liệu trên ngăn chặn UIViewControll, bạn chỉ gọi willMoveToParentViewControll nếu bạn ghi đè phương thức addChildViewContaptor. Trong ví dụ này tôi đang gọi nó nhưng không ghi đè.
timthetoolman

2
Trong bản demo tại WWDC tôi dường như nhớ rằng họ đã gọi nó trước khi gọi bắt đầu quá trình chuyển đổi, bởi vì quá trình chuyển đổi không ngụ ý rằng hiện tại PVC sẽ chuyển sang con số không. Trong trường hợp của UITabBarControll, quá trình chuyển đổi sẽ không thay đổi bất kỳ cha mẹ vc nào. Việc xóa khỏi cha mẹ gọi didMoveToParentViewControll: nil, nhưng sẽ không có cuộc gọi ... nào. IMHO
Felix Lamouroux

7

OK, tôi biết câu hỏi nói mà không sử dụng bộ điều khiển điều hướng, nhưng không có lý do gì để không. OP đã không trả lời các bình luận kịp thời để tôi đi ngủ. Đừng bỏ phiếu cho tôi. :)

Dưới đây là cách bật bộ điều khiển chế độ xem hiện tại và lật sang bộ điều khiển chế độ xem mới bằng bộ điều khiển điều hướng:

UINavigationController *myNavigationController = self.navigationController;
[[self retain] autorelease];

[myNavigationController popViewControllerAnimated:NO];

PreferencesViewController *controller = [[PreferencesViewController alloc] initWithNibName:nil bundle:nil];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.65];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:myNavigationController.view cache:YES];
[myNavigationController pushViewController:controller animated:NO];
[UIView commitAnimations];

[controller release];

Không ngăn xếp này xem bộ điều khiển?
TigerCoding

1
Có, bởi vì chúng tôi đang sử dụng bộ điều khiển điều hướng. Tuy nhiên, nó xoay quanh giới hạn về loại chuyển đổi bạn có thể thực hiện, mà tôi nghĩ là cốt lõi của câu hỏi của bạn.
Richard Brightwell

Đóng, nhưng một trong những vấn đề lớn là có nhiều bộ điều khiển xem để quản lý trên ngăn xếp. Tôi đang tìm cách thay đổi hoàn toàn bộ điều khiển xem. =)
TigerCoding

Ah. Tôi có thể có một cái gì đó tương tự ... cho tôi một phút. Nếu không, tôi sẽ xóa câu trả lời này.
Richard Brightwell

Ok, tôi đã ghép hai bit mã khác nhau lại với nhau. Tôi nghĩ rằng điều này sẽ làm những gì bạn muốn.
Richard Brightwell

3

Vì tôi vừa xảy ra vấn đề chính xác này và đã thử các biến thể trên tất cả các câu trả lời có sẵn để thành công hạn chế, tôi sẽ đăng bài về cách cuối cùng tôi đã giải quyết nó:

Như được mô tả trong bài viết này về các phân biệt tùy chỉnh , thực sự dễ dàng thực hiện các phân biệt tùy chỉnh. Chúng cũng rất dễ dàng kết nối trong Trình tạo giao diện, chúng giữ các mối quan hệ trong IB hiển thị và chúng không cần nhiều sự hỗ trợ của các bộ điều khiển xem nguồn / đích của segue.

Bài đăng được liên kết ở trên cung cấp mã iOS 4 để thay thế bộ điều khiển chế độ xem hàng đầu hiện tại trên ngăn xếp navigationContoder bằng một cái mới bằng cách sử dụng hình ảnh động trượt từ trên xuống.

Trong trường hợp của tôi, tôi muốn một sự thay thế tương tự xảy ra, nhưng với một FlipFromLeftsự chuyển đổi. Tôi cũng chỉ cần hỗ trợ cho iOS 5+. Mã số:

Từ RAFlipReplaceSegue.h:

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

Từ RAFlipReplaceSegue.m:

#import "RAFlipReplaceSegue.h"

@implementation RAFlipReplaceSegue

-(void) perform
{
    UIViewController *destVC = self.destinationViewController;
    UIViewController *sourceVC = self.sourceViewController;
    [destVC viewWillAppear:YES];

    destVC.view.frame = sourceVC.view.frame;

    [UIView transitionFromView:sourceVC.view
                        toView:destVC.view
                      duration:0.7
                       options:UIViewAnimationOptionTransitionFlipFromLeft
                    completion:^(BOOL finished)
                    {
                        [destVC viewDidAppear:YES];

                        UINavigationController *nav = sourceVC.navigationController;
                        [nav popViewControllerAnimated:NO];
                        [nav pushViewController:destVC animated:NO];
                    }
     ];
}

@end

Bây giờ, điều khiển kéo để thiết lập bất kỳ loại segue nào khác, sau đó biến nó thành một segue tùy chỉnh và nhập tên của lớp segue tùy chỉnh, et voilà!


IS có cách nào để lập trình này mà không có bảng phân cảnh không?
zakdances

Theo như tôi biết, để sử dụng một segue, bạn phải xác định nó và cung cấp cho nó một định danh trong bảng phân cảnh. Bạn có thể gọi một segue trong mã bằng –performSegueWithIdentifier:sender:phương thức của UIViewContoder .
Ryan Artecona

1
Bạn không bao giờ nên gọi viewDidXXX và viewWillXXX trực tiếp.
Ben Sinclair

2

Tôi đã vật lộn với vấn đề này trong một thời gian dài và một trong những vấn đề của tôi được liệt kê ở đây , tôi không chắc liệu bạn có gặp phải vấn đề đó không. Nhưng đây là những gì tôi muốn giới thiệu nếu nó phải hoạt động với iOS 4.

Thứ nhất, tạo một NavigationControllerlớp mới . Đây là nơi chúng ta sẽ thực hiện tất cả các công việc bẩn thỉu - các lớp khác sẽ có thể "dọn dẹp" các phương thức cá thể gọi như thế pushViewController:và như vậy. Trong của bạn .h:

@interface NavigationController : UIViewController {
    NSMutableArray *childViewControllers;
    UIViewController *currentViewController;
}

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion;
- (void)addChildViewController:(UIViewController *)childController;
- (void)removeChildViewController:(UIViewController *)childController;

Mảng điều khiển khung nhìn con sẽ đóng vai trò là một cửa hàng cho tất cả các bộ điều khiển khung nhìn trong ngăn xếp của chúng ta. Chúng tôi sẽ tự động chuyển tiếp tất cả các mã xoay và thay đổi kích thước từ NavigationControllerchế độ xem sang currentController.

Bây giờ, trong việc thực hiện của chúng tôi:

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion
{
    currentViewController = [toViewController retain];
    // Put any auto- and manual-resizing handling code here

    [UIView animateWithDuration:duration animations:animations completion:completion];

    [fromViewController.view removeFromSuperview];
}

- (void)addChildViewController:(UIViewController *)childController {
    [childViewControllers addObject:childController];
}

- (void)removeChildViewController:(UIViewController *)childController {
    [childViewControllers removeObject:childController];
}

Bây giờ bạn có thể thực hiện tùy chỉnh của riêng mình pushViewController:, popViewControllervà như vậy, bằng cách sử dụng các cuộc gọi phương thức này.

Chúc may mắn, và tôi hy vọng điều này sẽ giúp!


Một lần nữa, chúng ta phải sử dụng để thêm các bộ điều khiển xem dưới dạng các khung nhìn phụ của các bộ điều khiển xem hiện có. Cấp, đây là những gì Bộ điều khiển Điều hướng hiện có làm, nhưng điều đó có nghĩa là về cơ bản chúng ta phải viết lại nó và tất cả các phương thức của nó. Trong thực tế, chúng ta nên tránh phải phân phối viewWillAppear và các phương thức tương tự. Tôi bắt đầu nghĩ rằng không có cách nào sạch sẽ để làm điều này. Tuy nhiên, tôi cảm ơn bạn đã dành thời gian và nỗ lực!
TigerCoding

Tôi nghĩ, với hệ thống thêm và xóa bộ điều khiển xem này khi cần, giải pháp này ngăn bạn khỏi phải chuyển tiếp các phương thức đó.
aopsfan

Bạn có chắc không? Tôi đã từng trao đổi bộ điều khiển xem theo cách tương tự trước đây và tôi luôn phải chuyển tiếp tin nhắn. Bạn có thể xác nhận khác?
TigerCoding

Không, tôi không chắc chắn, nhưng tôi sẽ cho rằng, chừng nào bạn xóa chế độ xem các bộ điều khiển xem khi họ biến mất, và thêm chúng như chúng xuất hiện, đó sẽ tự động kích hoạt viewWillAppear, viewDidAppearvà như vậy.
aopsfan

-1
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

Hãy thử mã này.


Mã này cung cấp Chuyển đổi từ bộ điều khiển xem sang bộ điều khiển xem khác có bộ điều khiển điều hướng.

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.