네비게이션 컨트롤러 스택, 서브 뷰 또는 모달 컨트롤러를 사용하지 않고 뷰 컨트롤러의 애니메이션 변경?


142

NavigationController에는 관리 할 ViewController 스택과 제한된 애니메이션 전환이 있습니다.

기존의보기 컨트롤러에 하위보기로보기 컨트롤러를 추가하려면 이벤트를 하위보기 컨트롤러에 전달해야합니다. 하위보기 컨트롤러는 관리하기가 어렵고 약간의 성가심으로로드되며 일반적으로 구현시 나쁜 해킹처럼 느껴집니다 (Apple은 이 작업을 수행).

모달 뷰 컨트롤러를 다시 표시하면 뷰 컨트롤러가 다른 뷰 컨트롤러 위에 배치되며 위에서 설명한 이벤트 전달 문제가 없지만 실제로는 뷰 컨트롤러를 '스왑'하지 않고 스택합니다.

스토리 보드는 iOS 5로 제한되며 거의 이상적이지만 모든 프로젝트에서 사용할 수는 없습니다.

위의 제한없이 뷰 컨트롤러를 변경하고 애니메이션 전환을 허용하는 방법으로 SOLID CODE EXAMPLE을 제시 할 수 있습니까?

가까운 예제이지만 애니메이션은 없음 : 탐색 컨트롤러없이 여러 iOS 사용자 정의보기 컨트롤러를 사용하는 방법

편집 : Nav Controller 사용은 좋지만 슬라이드 효과가 아닌 애니메이션 전환 스타일이 있어야합니다.보기 컨트롤러가 완전히 스왑되어야합니다 (스택되지 않음). 두 번째 뷰 컨트롤러가 스택에서 다른 뷰 컨트롤러를 제거해야하는 경우 캡슐화되지 않은 것입니다.

편집 2 : iOS 4 가이 질문의 기본 OS 여야합니다. 스토리 보드를 언급 할 때 위의 내용을 분명히 했어야합니다.


1
탐색 컨트롤러를 사용하여 사용자 정의 애니메이션 전환을 수행 할 수 있습니다. 이것이 괜찮다면 질문에서 해당 제약 조건을 제거하고 코드 예제를 게시하겠습니다.
Richard Brightwell

@Richard 스택 관리의 번거 로움을 건너 뛰고 뷰 컨트롤러간에 다른 애니메이션 전환 스타일을 수용하면 내비게이션 컨트롤러 사용이 좋습니다!
TigerCoding

그래 좋아. 참을성이 없어서 코드를 게시했습니다. 시도 해봐. 나를 위해 작동합니다.
Richard Brightwell

@RichardBrightwell 당신은 내비게이션 컨트롤러를 사용하여 뷰 컨트롤러간에 커스텀 애니메이션 전환을 할 수 있다고 말했습니다 ... 어떻게? 예를 게시 할 수 있습니까? 감사.
Duck

답변:


108

편집 : 모든 방향에서 작동하는 새로운 답변. 원래 답변은 인터페이스가 세로 방향 일 때만 작동합니다. 이것은 다른 뷰가있는 뷰를 대체하는 b / c 뷰 전환 애니메이션으로, 창에 추가 된 첫 번째 뷰 아래의 레벨 (예 :) 이상이어야합니다window.rootViewController.view.anotherView.

내가 호출 한 간단한 컨테이너 클래스를 구현했습니다 TransitionController. https://gist.github.com/1394947 에서 찾을 수 있습니다 .

따로, 별도의 클래스 b / c로 구현하는 것이 더 쉽습니다. 재사용하기 쉽습니다. 원하지 않으면 TransitionController클래스에 대한 필요성을 제거하면서 앱 델리게이트에서 동일한 로직을 직접 구현할 수 있습니다 . 그러나 필요한 논리는 동일합니다.

다음과 같이 사용하십시오.

앱 위임에서

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

모든 뷰 컨트롤러에서 새 뷰 컨트롤러로 전환하려면

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

편집 : 아래의 원래 답변-portait 방향에서만 작동합니다

이 예제에서는 다음과 같은 가정을했습니다.

  1. rootViewController창에 뷰 컨트롤러가 할당되어 있습니다.

  2. 새보기로 전환하면 현재 viewController를 새보기를 소유 한 viewController로 바꾸려고합니다. 언제든지 현재 viewController 만 활성화됩니다 (예 : 할당 됨).

코드는 다르게 작동하도록 쉽게 수정할 수 있습니다. 요점은 애니메이션 전환 및 단일 뷰 컨트롤러입니다. 뷰 컨트롤러를에 할당하는 것 외에는 아무 곳에 나 보관하지 마십시오 window.rootViewController.

앱 델리게이트에서 전환을 애니메이션으로 만드는 코드

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

뷰 컨트롤러에서의 사용 예

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

1
응 매우 좋아! 트릭을 수행 할뿐만 아니라 매우 간단하고 깨끗한 코드 예제입니다. 많은 감사합니다!
TigerCoding

그의 풍경에 문제를 일으키지 않습니까? 또한 : 이것이 viewController의 willAppear 및 didAppear 메소드를 트리거합니까?
Felix Lamouroux

1
통화를 기록했기 때문에 통화가 완료된 것으로 알고 있습니다. 또한 이것이 왜 방향 변경에 영향을 미치는지 알지 못합니다. 왜 그렇게 생각하는지 자세히 설명 할 수 있습니까?
TigerCoding

1
@XJones 훌륭한 솔루션은 처음 초기화 한 후 직면하고있는 하나의 문제를 제외하고 iPad 앱에서 잘 작동합니다. transVC = [TransitionController ... initWithViewController : aUINavCtrlObj]; window.rootVC = transVC; aUINavCtrlObj의보기는 위에서 20px로 채워집니다 (즉, 아래쪽 20px는 모든 방향에서 화면 (UIWindow) 범위를 벗어났습니다). 그러나 [transVC transitionToViewController : anotherVC]를 수행하면 패딩이 사라집니다. TransitionController의 loadView에서 wantsFullScreenLayout = NO를 시도했지만 statusBar 바로 아래에 20px 검은 색 영역이 추가됩니다.
Abduliam Rehmanius

1
@ AbduliamRehmanius : 나는 같은 문제가 있었다. 25 행을로 변경 TransitionController.m하여 문제 UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];를 해결했지만 최신 버전의 iOS에서만 사용 했으므로 신중하게 테스트하세요.
Phil Calvin

67

Apple의 새로운 viewController 격리 시스템을 사용할 수 있습니다. 보다 자세한 정보는 WWDC 2011 세션 비디오 "Implementing UIViewControllerContainment"를 확인하십시오.

iOS5에 새로 도입 된 UIViewControllerContainment를 사용하면 부모 viewController와 그 안에 포함 된 여러 자식 viewController를 가질 수 있습니다. 이것이 UISplitViewController의 작동 방식입니다. 이렇게하면 뷰 컨트롤러를 부모에 스택 할 수 있지만 특정 응용 프로그램의 경우 부모를 사용하여 하나의 보이는 viewController에서 다른 viewController 로의 전환을 관리하고 있습니다. 이것은 애플이 승인 한 일을하고 하나의 자식 viewController에서 애니메이션을 만드는 방법입니다. 또한 다양한 UIViewAnimationOption전환을 모두 사용할 수 있습니다 !

또한 UIViewContainment를 사용하면 오리엔테이션 이벤트 중에 자식 viewController를 관리해야하는 지저분함에 대해 걱정할 필요가 없습니다. 다음을 사용하여 parentViewController가 회전 이벤트를 자식 viewController로 전달하도록 할 수 있습니다.

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

부모의 viewDidLoad 메서드에서 다음과 유사한 작업을 수행하여 첫 번째 childViewController를 설정할 수 있습니다.

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

그런 다음 자식 viewController를 변경해야 할 때 부모 viewController 내에서 다음 행을 따라 무언가를 호출하십시오.

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

https://github.com/toolmanGitHub/stackedViewControllers에 전체 예제 프로젝트를 게시했습니다 . 이 다른 프로젝트UIViewController전체 화면을 차지하지 않는 다양한 입력 viewController 유형에서 Containment 를 사용하는 방법을 보여줍니다 . 행운을 빕니다


1
좋은 예입니다. 시간을내어 코드를 게시 해 주셔서 감사합니다. 반면, iOS 5로 제한됩니다. 질문에서 언급했듯이 "[스토리 보드]는 iOS 5로 제한되어 있으며 거의 ​​이상적이지만 모든 프로젝트에 사용할 수는 없습니다." 많은 고객 (약 40 %)이 여전히 iOS 4를 사용한다는 점을 고려하면 iOS 4 이상에서 작동하는 것을 제공하는 것이 목표입니다.
TigerCoding

[self.currentViewController willMoveToParentViewController:nil];전환하기 전에 전화하지 않아야 합니까?
Felix Lamouroux

@FelixLam-UIViewController 포함에 대한 문서마다 addChildViewController 메소드를 재정의하는 경우에만 willMoveToParentViewController를 호출합니다. 이 예제에서는 호출하지만 재정의하지는 않습니다.
timthetoolman

2
WWDC의 데모에서는 전환을 시작하기 전에 전화를 걸 었음을 기억합니다. 전환은 currentVC가 nil로 이동한다는 것을 암시하지 않기 때문입니다. UITabBarController의 경우 전이는 vc의 부모를 변경하지 않습니다. 부모에서 제거하면 didMoveToParentViewController : nil을 호출하지만 will ... 호출은 없습니다. IMHO
Felix Lamouroux

7

좋아, 질문은 네비게이션 컨트롤러를 사용하지 않고 말하지만 아무 이유가 없다는 것을 알고 있습니다. OP가 잠자기 시간에 댓글에 응답하지 않았습니다. 투표하지 마십시오. :)

탐색 컨트롤러를 사용하여 현재 뷰 컨트롤러를 팝하고 새 뷰 컨트롤러로 전환하는 방법은 다음과 같습니다.

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

이 스택 뷰 컨트롤러가 아닙니까?
TigerCoding

1
네, 네비게이션 컨트롤러를 사용하고 있기 때문입니다. 그러나 어떤 종류의 전환을 수행 할 수 있는지에 대한 제한을 극복했습니다. 이는 귀하의 질문의 핵심이라고 생각했습니다.
Richard Brightwell

닫기는하지만 큰 문제 중 하나는 스택에서 관리 할 여러 개의보기 컨트롤러가 있다는 것입니다. 뷰 컨트롤러를 완전히 변경하는 방법을 찾고 있습니다. =)
TigerCoding 2011

아 나도 그런 것을 가지고 있을지도 모른다 ... 잠시만 기다려주세요. 그렇지 않은 경우이 답변을 삭제하겠습니다.
Richard Brightwell

좋아, 나는 두 개의 다른 코드 비트를 함께 모았다. 나는 이것이 당신이 원하는 것을 할 것이라고 생각합니다.
Richard Brightwell

3

방금이 정확한 문제를 겪고 제한된 성공에 대한 기존의 모든 대답에 대한 변형을 시도했기 때문에 결국 어떻게 해결했는지 게시 할 것입니다.

이 사용자 정의 segues대한 게시물에서 설명했듯이 실제로 사용자 정의 segue를 만드는 것은 실제로 쉽습니다. 또한 Interface Builder에서 매우 쉽게 연결할 수 있으며 IB의 관계를 계속 볼 수 있으며 segue의 소스 / 대상 뷰 컨트롤러에서 많은 지원을 필요로하지 않습니다.

위에 링크 된 게시물은 내비게이션 컨트롤러 스택의 현재 상단 뷰 컨트롤러를 슬라이드-인-투-탑 애니메이션을 사용하여 새 것으로 교체하기위한 iOS 4 코드를 제공합니다.

필자의 경우 비슷한 교체 segue가 발생하지만 FlipFromLeft전환이 필요했습니다. 또한 iOS 5 이상에 대한 지원 만 필요했습니다. 암호:

RAFlipReplaceSegue.h에서 :

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

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

이제 Control- 드래그하여 다른 종류의 segue를 설정 한 다음 Custom segue로 만들고 custom segue 클래스의 이름을 입력하십시오.


스토리 보드없이 프로그래밍 방식 으로이 방법을 사용할 수 있습니까?
zakdances

내가 아는 한, segue를 사용하려면 스토리를 정의하고 스토리 보드에서 식별자를 제공해야합니다. UIViewController의 –performSegueWithIdentifier:sender:메소드를 사용 하여 코드에서 segue를 호출 할 수 있습니다 .
Ryan Artecona

1
viewDidXXX 및 viewWillXXX를 직접 호출해서는 안됩니다.
Ben Sinclair

2

나는 오랫동안이 문제로 고생했으며 내 문제 중 하나가 여기 에 나열되어 있습니다. 문제가 있는지 확실하지 않습니다. 그러나 iOS 4에서 작동 해야하는 경우 권장하는 사항은 다음과 같습니다.

먼저 새 NavigationController클래스를 만듭니다 . 이것은 우리가 모든 더러운 작업을 수행하는 곳입니다. 다른 클래스는 인스턴스 메소드 pushViewController:등 을 "정리 적으로"호출 할 수 있습니다 . 당신의 .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;

하위 뷰 컨트롤러 배열은 스택의 모든 뷰 컨트롤러에 대한 저장소 역할을합니다. 우리는 모든 회전 및 크기 조정 코드를 NavigationController'보기 에서 '로 자동 전달 합니다 currentController.

이제 구현에서 :

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

지금 당신은 당신의 자신의 주문을 구현할 수 있습니다 pushViewController:, popViewController이러한 메소드 호출을 사용하여, 등.

행운을 빌어 요, 이것이 도움이되기를 바랍니다!


다시 한 번 뷰 컨트롤러를 기존 뷰 컨트롤러의 하위 뷰로 추가해야합니다. 물론 이것이 기존 내비게이션 컨트롤러의 기능이지만 기본적으로 다시 작성해야한다는 것을 의미합니다. 실제로 viewWillAppear 및 유사한 메소드를 배포하지 않아도됩니다. 나는 이것을하는 확실한 방법이 없다고 생각하기 시작했습니다. 그러나 시간과 노력을 주셔서 감사합니다!
TigerCoding

필요에 따라 뷰 컨트롤러를 추가 및 제거하는이 시스템을 사용하면이 솔루션을 사용하여 해당 방법을 전달하지 않아도됩니다.
aopsfan

확실합니까? 전에 비슷한 방식으로 뷰 컨트롤러를 교체하는 데 사용했으며 항상 메시지를 전달해야했습니다. 그렇지 않으면 확인할 수 있습니까?
TigerCoding

아니 그해야 자동 트리거, 잘 모르겠어요,하지만 그들은 사라질만큼 당신이보기 컨트롤러의 뷰를 제거뿐만 나는 그 가정 것이다, 그들은 나타나지 않는 한 추가 viewWillAppear, viewDidAppear, 등.
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];

이 코드를 사용해보십시오.


이 코드는 뷰 컨트롤러에서 내비게이션 컨트롤러가있는 다른 뷰 컨트롤러로의 전환을 제공합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.