탐색 기반 앱에서 푸시 및 팝 애니메이션을 변경하는 방법


221

탐색 기반 응용 프로그램이 있고 푸시 및 팝 애니메이션의 애니메이션을 변경하고 싶습니다. 어떻게해야합니까?

2018 수정

이 질문에 대한 많은 답변이 있었으며 지금은 꽤 오래 걸렸습니다. 지금 가장 관련성이 있다고 생각되는 것에 대한 답변을 다시 선택했습니다. 다르게 생각하는 사람이 있으면 의견을 남겨주세요.


25
iOS 7부터는 공식 API가 있습니다. UINavigationControllerDelegate 의 커스텀 전환 애니메이션 지원을 참조하십시오 . 이에 관한 WWDC 2013 비디오 도 있습니다 .
Jesse Rusak

Swift 에서이 작업을 수행하기위한 답변 (아래)을 추가했습니다 .Swift 구현에 대해 묻는이 질문을 보았으므로 후속 구현과 함께 차임 할 것이라고 생각했습니다.
djbp

1
공식 (iOS 7+) API를 사용한 매우 유용한 자습서는 다음을 참조하십시오. bradbambara.wordpress.com/2014/04/11/…
nikolovski

1
WWDC 2013 비디오에 업데이트 된 링크를 @JesseRusak : developer.apple.com/videos/play/wwdc2013-218
요이치 Rutkowski에게

1
허용되는 답변을 n gals로 변경했습니다. 도움이 되었기를 바랍니다! GLHF

답변:


35

탐색 기반 앱에서 푸시 및 팝 애니메이션을 변경하는 방법 ...

2019 년에는 "최종 답변!"

전문:

iOS 개발에 익숙하지 않다고 가정 해보십시오. 혼란스럽게도 Apple은 쉽게 사용할 수있는 두 가지 전환을 제공합니다. "crossfade"와 "flip"이 있습니다.

그러나 "crossfade"와 "flip"은 쓸모가 없습니다. 그들은 결코 사용되지 않습니다. 애플이 왜이 두 가지 쓸모없는 전환을 제공했는지는 아무도 모른다!

그래서:

"슬라이드"와 같이 일반적인 일반적인 전환 을 수행하려고한다고 가정합니다 . 이 경우 엄청난 양의 작업을 수행해야합니다! .

이 작업은이 게시물에 설명되어 있습니다.

반복하기 만하면됩니다.

놀랍게도 : iOS를 사용하면 가장 단순하고 일반적인 일상 전환 (예 : 일반 슬라이드)을 원한다면 전체 사용자 정의 전환 을 구현하는 모든 작업 이 필요합니다 .

방법은 다음과 같습니다.

1. 당신은 관례가 필요합니다 UIViewControllerAnimatedTransitioning

  1. 당신은 자신의 부울이 필요합니다 popStyle. (튀어 나오거나 터지는가?)

  2. transitionDuration(사소한) 주요 전화를 포함해야합니다 .animateTransition

  3. 실제로 inside에 대해 두 가지 다른 루틴을 작성 해야합니다animateTransition . 하나는 푸시 용이고 다른 하나는 팝용입니다. 아마 그 이름 animatePushanimatePop. 내부 에서 두 루틴으로 animateTransition분기하십시오.popStyle

  4. 아래 예제는 간단한 이동 / 이동을 수행합니다.

  5. 당신 animatePushanimatePop일상에서. 당신은 해야한다 은 "보기"및 "보기에"를 얻는다. (이를 수행하는 방법은 코드 예제에 나와 있습니다.)

  6. 새로운 "to"보기 를 사용해야합니다 addSubview .

  7. 그리고 당신 completeTransition 애니메이션의 끝에서 전화 해야합니다

그래서 ..

  class SimpleOver: NSObject, UIViewControllerAnimatedTransitioning {
        
        var popStyle: Bool = false
        
        func transitionDuration(
            using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.20
        }
        
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            if popStyle {
                
                animatePop(using: transitionContext)
                return
            }
            
            let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
            let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
            
            let f = transitionContext.finalFrame(for: tz)
            
            let fOff = f.offsetBy(dx: f.width, dy: 55)
            tz.view.frame = fOff
            
            transitionContext.containerView.insertSubview(tz.view, aboveSubview: fz.view)
            
            UIView.animate(
                withDuration: transitionDuration(using: transitionContext),
                animations: {
                    tz.view.frame = f
            }, completion: {_ in 
                    transitionContext.completeTransition(true)
            })
        }
        
        func animatePop(using transitionContext: UIViewControllerContextTransitioning) {
            
            let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
            let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
            
            let f = transitionContext.initialFrame(for: fz)
            let fOffPop = f.offsetBy(dx: f.width, dy: 55)
            
            transitionContext.containerView.insertSubview(tz.view, belowSubview: fz.view)
            
            UIView.animate(
                withDuration: transitionDuration(using: transitionContext),
                animations: {
                    fz.view.frame = fOffPop
            }, completion: {_ in 
                    transitionContext.completeTransition(true)
            })
        }
    }

그리고 ...

2. 뷰 컨트롤러에서 사용하십시오.

참고 : 이상하게도 "첫 번째"보기 컨트롤러 에서만이 작업을 수행해야합니다 . (아래에있는 것)

당신이 상단에 팝업 하나 , 아무것도 하지 마십시오 . 쉬운.

수업은 ...

class SomeScreen: UIViewController {
}

된다 ...

class FrontScreen: UIViewController,
        UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
    
    let simpleOver = SimpleOver()
    

    override func viewDidLoad() {
        
        super.viewDidLoad()
        navigationController?.delegate = self
    }

    func navigationController(
        _ navigationController: UINavigationController,
        animationControllerFor operation: UINavigationControllerOperation,
        from fromVC: UIViewController,
        to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        
        simpleOver.popStyle = (operation == .pop)
        return simpleOver
    }
}

그게 다야.

정상적으로 정상적으로 밀고 터지십시오. 밀어 ...

let n = UIStoryboard(name: "nextScreenStoryboardName", bundle: nil)
          .instantiateViewController(withIdentifier: "nextScreenStoryboardID")
          as! NextScreen
navigationController?.pushViewController(n, animated: true)

다음 화면에서 원하는 경우 팝업을 표시 할 수 있습니다.

class NextScreen: TotallyOrdinaryUIViewController {
    
    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {
        
        navigationController?.popViewController(animated: true)
    }
}


3.이 페이지에서 AnimatedTransitioning을 재정의하는 방법을 설명하는 다른 답변도 즐기십시오

AnimatedTransitioning요즘 iOS 앱 사용 방법에 대한 자세한 내용은 @AlanZeino 및 @elias 답변으로 스크롤하십시오 !


우수한! 탐색 스 와이프 제스처를 지원하려면 동일한 AnimatedTransitioning도 지원하십시오. 어떤 생각?
sam chi wen

@samchiwen 감사합니다-실제로 그것은 정확히 무엇 animatePush이며 animatePop.. 두 가지 다른 방향!
Fattie

268

나는 다음을했고 잘 작동합니다 .. 간단하고 이해하기 쉽습니다 ..

CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
//transition.subtype = kCATransitionFromTop; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[[self navigationController] popViewControllerAnimated:NO];

그리고 똑같은 것도 ..


스위프트 3.0 버전 :

let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.navigationController?.view.layer.add(transition, forKey: nil)
_ = self.navigationController?.popToRootViewController(animated: false)

34
+1, 이것이 가장 제정신의 해결책입니다. 미래 방문자를위한 작은 참고 사항 :이 Animated:NO부분은 매우 중요합니다. 경우 YES전달, 애니메이션 믹스 재미있는 효과를 야기한다.
DarkDust

12
지금까지 가장 좋은 솔루션입니다. 초보자에게는 QuartCore (#import <QuartzCore / QuartzCore.h>)를 포함시키는 것을 잊지 마십시오
nomann

4
이 솔루션에 대한 유일한 문제는 애니메이션없이 푸시 한 직후 푸시 된 viewcontroller의 viewDidAppear가 호출되는 것입니다. 그 주위에 방법이 있습니까?
Pedro Mancheno

9
이 코드의 내 문제는 각보기가 미끄러지거나 꺼질 때 회색 또는 흰색으로 깜박이는 것처럼 보입니다.
크리스

1
iOS 7.1.2 및 iOS 8.3에서 확인 됨 —이 코드는 메소드에서도 잘 작동합니다.setViewControllers:
proff

256

이것이 내가 항상이 작업을 완료 한 방법입니다.

푸시의 경우 :

MainView *nextView=[[MainView alloc] init];
[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[self.navigationController pushViewController:nextView animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
[UIView commitAnimations];
[nextView release];

팝의 경우 :

[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView commitAnimations];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.375];
[self.navigationController popViewControllerAnimated:NO];
[UIView commitAnimations];


나는 여전히 이것으로부터 많은 피드백을 얻으므로 어쨌든 애니메이션을 수행하는 Apple 권장 방법 인 애니메이션 블록을 사용하도록 업데이트 할 것입니다.

푸시의 경우 :

MainView *nextView = [[MainView alloc] init];
[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [self.navigationController pushViewController:nextView animated:NO];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
                         }];

팝의 경우 :

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];

3
고마워 그러나 팝은 UINavigationController에 의해 자동으로 수행됩니다. 사용자 정의 팝 로직을 호출 할 수 있도록 해당 동작을 어떻게 재정의합니까?
Joshua Frank

1
@stuckj 실제로 작동합니다! 당신은 교체해야 super으로self.navigationController
holierthanthou84

오른쪽에서 기본 슬라이드 대신 왼쪽에서 슬라이드를 가져 오는 방법이 있습니까?
shim

첫 번째는 새로운 관점을 전혀 보여주지 않습니다. 두 번째는 애니메이션을 표시하지 않습니다. 아주 나쁜 대답입니다! iOS 7.
Dmitry

2
UIViewController"ViewController"부분없이 이름 을 서브 클래스로 제공 한 이유는 무엇입니까 ? 이 이름은 UIView에 더 적합합니다.
user2159978

29

푸시

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
//transition.subtype = kCATransitionFromTop;

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController pushViewController:ViewControllerYouWantToPush animated:NO];

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
//transition.subtype = kCATransitionFromTop;

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];

19

@Magnus는 Swift (2.0)에 대해서만 대답합니다.

    let transition = CATransition()
    transition.duration = 0.5
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromTop
    self.navigationController!.view.layer.addAnimation(transition, forKey: nil)
    let writeView : WriteViewController = self.storyboard?.instantiateViewControllerWithIdentifier("WriteView") as! WriteViewController
    self.navigationController?.pushViewController(writeView, animated: false)

일부 주석 :

Segue와 함께이 작업을 수행 할 수 있습니다 . prepareForSegue또는 에서 구현하십시오 shouldPerformSegueWithIdentifier. 하나 기본 애니메이션도 그대로 유지됩니다. 이 문제를 해결하려면 스토리 보드로 이동하여 Segue를 클릭하고 'Animates'상자를 선택 취소하십시오. 그러나 이것은 IOS 9.0 이상의 앱을 제한합니다 (Xcode 7에서 가장 좋았습니다).

segue에서 수행 할 때 마지막 두 줄은 다음과 같이 바꿔야합니다.

self.navigationController?.popViewControllerAnimated(false)

내가 잘못 설정했지만 무시합니다.


애니메이션이 끝날 때 배경에서 검은 색을 제거하는 방법.
Madhu

팝 뷰 컨트롤러에 대한 푸시 뷰 컨트롤러 애니메이션 작품이 작동하지 않음
Mukul을 더

16

에 그 기억 스위프트 , 확장은 확실히 당신의 친구!

public extension UINavigationController {

    /**
     Pop current view controller to previous view controller.

     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func pop(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.popViewControllerAnimated(false)
    }

    /**
     Push a new view controller on the view controllers's stack.

     - parameter vc:       view controller to push.
     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func push(viewController vc: UIViewController, transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.pushViewController(vc, animated: false)
    }

    private func addTransition(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = type
        self.view.layer.addAnimation(transition, forKey: nil)
    }

}

11

Apple이 더 이상 앱을 승인하지 않기 때문에 개인 통화를 사용하는 것은 좋지 않습니다. 아마도 당신은 이것을 시도 할 수 있습니다 :

//Init Animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.50];


[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.navigationController.view cache:YES];

//Create ViewController
MyViewController *myVC = [[MyViewController alloc] initWith...];

[self.navigationController pushViewController:myVC animated:NO];
[myVC release];

//Start Animation
[UIView commitAnimations];

"반만"작동합니다. 팝 애니메이션의 어려운 문제를 해결하지 못합니다.
Adam

나는이 솔루션을 더 좋아하고 그렇습니다. 개인적인 방법을 사용하면 확실히 거부됩니다.
Benjamin Intal

개인 API 호출 인 @nicktmro. 나는 눈치 채지 못했다.
Franklin

@ 프랭클린 (Franklin) @ 사용하기에 관한 토론이 있었는데 -pushViewController:transition:forceImmediate:나쁜 생각 일 것입니다.
nicktmro

9

이것이 Google의 최고 결과이기 때문에 제가 생각하는 것을 가장 제정신이라고 생각합니다. iOS 7+ 전환 API를 사용하는 것입니다. Swift 3을 사용하여 iOS 10에 대해 이것을 구현했습니다.

UINavigationController서브 클래스의 서브 클래스를 작성 UINavigationController하고 UIViewControllerAnimatedTransitioning프로토콜 을 준수하는 클래스의 인스턴스를 리턴하는 경우이를 두 개의보기 컨트롤러 사이의 애니메이션 방식과 결합하는 것은 매우 간단 합니다.

예를 들어 여기 내 UINavigationController하위 클래스가 있습니다.

class NavigationController: UINavigationController {
    init() {
        super.init(nibName: nil, bundle: nil)

        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension NavigationController: UINavigationControllerDelegate {

    public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return NavigationControllerAnimation(operation: operation)
    }

}

내가 UINavigationControllerDelegate자체로 설정 한 것을 볼 수 있고 , 서브 클래스의 확장 UINavigationControllerDelegate에서는 사용자 정의 애니메이션 컨트롤러 (예 :)를 반환 할 수 있는 메소드를 구현합니다 NavigationControllerAnimation. 이 사용자 정의 애니메이션 컨트롤러가 스톡 애니메이션을 대신합니다.

NavigationControllerAnimation초기화를 통해 인스턴스에 작업을 전달하는지 궁금 할 것입니다 . 나는 프로토콜 NavigationControllerAnimation의 구현 UIViewControllerAnimatedTransitioning에서 작업이 무엇인지 (즉, '푸시'또는 '팝') 알 수 있도록 이렇게합니다. 이것은 내가 어떤 종류의 애니메이션을해야하는지 아는 데 도움이됩니다. 대부분의 경우 작업에 따라 다른 애니메이션을 수행하려고합니다.

나머지는 꽤 표준입니다. UIViewControllerAnimatedTransitioning프로토콜 에서 두 가지 필수 기능을 구현하고 원하는대로 애니메이션을 적용하십시오.

class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {

    let operation: UINavigationControllerOperation

    init(operation: UINavigationControllerOperation) {
        self.operation = operation

        super.init()
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
        let containerView = transitionContext.containerView

        if operation == .push {
            // do your animation for push
        } else if operation == .pop {
            // do your animation for pop
        }
    }
}

각기 다른 유형의 작업 (예 : '푸시'또는 '팝')에 대해 뷰 컨트롤러와 뷰 컨트롤러가 서로 다르다는 것을 기억해야합니다. 푸시 조작 중에는 to view 컨트롤러가 푸시됩니다. 팝 작업 중에는 to view 컨트롤러가 전환되는 컨트롤러가되고 from view 컨트롤러가 팝 컨트롤러가됩니다.

또한 to뷰 컨트롤러는 containerView전환 컨텍스트에서 하위 뷰로 추가해야합니다 .

애니메이션이 완료되면를 호출해야합니다 transitionContext.completeTransition(true). 대화 형 전환을 수행하는 경우, 당신은 동적를 반환해야합니다 Bool으로 completeTransition(didComplete: Bool)전환이 애니메이션의 끝에 완료된 경우에 따라.

마지막으로 ( 선택 사항 읽기 ), 내가 작업했던 전환을 어떻게 수행했는지보고 싶을 수도 있습니다. 이 코드는 조금 더 해키이며 꽤 빨리 작성하여 훌륭한 애니메이션 코드라고 말하지는 않지만 여전히 애니메이션 부분을 수행하는 방법을 보여줍니다.

광산은 정말 간단한 전환이었습니다. UINavigationController가 일반적으로하는 것과 동일한 애니메이션을 모방하고 싶었지만 '다음 페이지 위에있는'애니메이션 대신 새로운 뷰와 동시에 이전 뷰 컨트롤러의 1 : 1 애니메이션을 구현하고 싶었습니다. 컨트롤러가 나타납니다. 이는 두 개의 뷰 컨트롤러가 서로 고정되어있는 것처럼 보이게하는 효과가 있습니다.

푸시 작업의 경우 먼저 toViewControllerx 축 오프 화면에서의 뷰 원점을 설정 하고을 서브 뷰로 추가하고이를 0 containerView으로 설정하여 화면에 애니메이션을 적용해야 origin.x합니다. 동시에 화면에서 화면을 꺼서 fromViewController보기를 움직이게합니다 origin.x.

toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                toViewController.view.frame = containerView.bounds
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

팝 연산은 기본적으로 반대입니다. 추가 toViewController의 하위 뷰로서 containerView, 그리고 멀리 애니메이션 fromViewController당신이에서 애니메이션으로 오른쪽 toViewController왼쪽에서 :

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
                toViewController.view.frame = containerView.bounds
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

전체 신속한 파일이있는 요지가 있습니다.

https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be


큰!. 내가하고 싶었던 것은 반대 방향으로 애니메이션하는 것입니다. 다른 솔루션을 검토했지만 왼쪽과 오른쪽 화면에서 모두 깜박입니다. 암시 적 알파 변경 애니메이션을 제거 할 수없는 것 같습니다. 이 솔루션 만 문제를 해결했습니다.
beshio

예, 이것이 유일한 최신 현대 솔루션입니다. (나는 상관 없지만 아래에 입력 한 솔루션과 정확히 동일합니다! :))
Fattie

@AlanZeino 만약 같은 ViewController 안에 다른 버튼 클릭을 위해 다른 애니메이션이 필요하다면 어떻게해야합니까? 따라서 button1의 경우 디졸브 애니메이션이 필요하고 button2의 경우 기본 전환이 필요합니다.
jzeferino

7

UINavigationControllerDelegate 및 UIViewControllerAnimatedTransitioning이 있으며 원하는대로 애니메이션을 변경할 수 있습니다.

예를 들어 VC의 수직 팝 애니메이션입니다.

@objc class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 0.5
}

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

    let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
    let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let containerView = transitionContext.containerView()
    let bounds = UIScreen.mainScreen().bounds
    containerView!.insertSubview(toViewController.view, belowSubview: fromViewController.view)
    toViewController.view.alpha = 0.5

    let finalFrameForVC = fromViewController.view.frame

    UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
        fromViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.height)
        toViewController.view.alpha = 1.0
        }, completion: {
            finished in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
    })
}

}

그리고

func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .Pop {
        return PopAnimator()
    }
    return nil;
}

유용한 튜토리얼 https://www.objc.io/issues/5-ios7/view-controller-transitions/


6

swift 4 에 대한 업데이트 된 답변을 바탕으로jordanperry

푸시 UIViewController

let yourVC = self.storyboard?.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController
    UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    self.navigationController?.pushViewController(terms, animated: true)
    UIView.setAnimationTransition(.flipFromRight, for: (self.navigationController?.view)!, cache: false)
})

UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false)
})
navigationController?.popViewController(animated: false)

5

다음은 Swift에서 동일한 작업을 수행 한 방법입니다.

푸시의 경우 :

    UIView.animateWithDuration(0.75, animations: { () -> Void in
        UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
        self.navigationController!.pushViewController(nextView, animated: false)
        UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromRight, forView: self.navigationController!.view!, cache: false)
    })

팝의 경우 :

실제로 위의 일부 응답과는 약간 다르게이 작업을 수행했지만 Swift 개발을 처음 접했을 때 올바르지 않을 수 있습니다. viewWillDisappear:animated:팝 코드를 재정의 하고 추가했습니다.

    UIView.animateWithDuration(0.75, animations: { () -> Void in
        UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
        UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false)
    })

    super.viewWillDisappear(animated)

5

스위프트 4.2 @ @Luca Davanzo의 답변

public extension UINavigationController {

    /**
     Pop current view controller to previous view controller.

     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func pop(transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.popViewController(animated: false)
    }

    /**
     Push a new view controller on the view controllers's stack.

     - parameter vc:       view controller to push.
     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func push(viewController vc: UIViewController, transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.pushViewController(vc, animated: false)
    }

    private func addTransition(transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        transition.type = type
        self.view.layer.add(transition, forKey: nil)
    }

}

4

나는 최근에 비슷한 것을하려고했습니다. 나는 UINavigationController의 슬라이딩 애니메이션을 좋아하지 않기로 결정했지만 UIView가 컬이나 이와 유사한 것을주는 애니메이션을하고 싶지 않았습니다. 밀거나 터질 때 뷰 사이에서 크로스 페이드를하고 싶었습니다.

문제는 뷰가 말 그대로 뷰를 제거하거나 현재 뷰의 상단에 터지는 것이므로 페이드가 작동하지 않는다는 사실과 관련이 있습니다. 해결책은 새보기를 가져 와서 UIViewController 스택의 현재 상위보기에 하위보기로 추가하는 것입니다. 알파 0으로 추가 한 다음 크로스 페이드를 수행합니다. 애니메이션 시퀀스가 ​​끝나면 뷰를 애니메이션하지 않고 스택에 밀어 넣습니다. 그런 다음 이전 topView로 돌아가 변경 한 내용을 정리합니다.

탐색 항목이 있으므로 전환이 올바르게 보이도록 조정해야하기 때문에 그보다 조금 더 복잡합니다. 또한 회전을 수행하는 경우 뷰를 하위 뷰로 추가 할 때 프레임 크기가 화면에 올바르게 표시되도록 프레임 크기를 조정해야합니다. 다음은 내가 사용한 코드 중 일부입니다. UINavigationController를 서브 클래스 화하고 push 및 pop 메소드를 대체했습니다.

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
      UIViewController *currentViewController = [self.viewControllers lastObject];
      //if we don't have a current controller, we just do a normal push
      if(currentViewController == nil)
      {
         [super pushViewController:viewController animated:animated];
         return;
      }
      //if no animation was requested, we can skip the cross fade
      if(!animation)
      {
         [super pushViewController:viewController animated:NO];
         return;
      }
      //start the cross fade.  This is a tricky thing.  We basically add the new view
//as a subview of the current view, and do a cross fade through alpha values.
//then we push the new view on the stack without animating it, so it seemlessly is there.
//Finally we remove the new view that was added as a subview to the current view.

viewController.view.alpha = 0.0;
//we need to hold onto this value, we'll be releasing it later
    NSString *title = [currentViewController.title retain];

//add the view as a subview of the current view
[currentViewController.view addSubview:viewController.view];
[currentViewController.view bringSubviewToFront:viewController.view];
UIBarButtonItem *rButtonItem = currentViewController.navigationItem.rightBarButtonItem;
UIBarButtonItem *lButtonItem = currentViewController.navigationItem.leftBarButtonItem;

NSArray *array = nil;

//if we have a right bar button, we need to add it to the array, if not, we will crash when we try and assign it
//so leave it out of the array we are creating to pass as the context.  I always have a left bar button, so I'm not checking to see if it is nil. Its a little sloppy, but you may want to be checking for the left BarButtonItem as well.
if(rButtonItem != nil)
    array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,rButtonItem,nil];
else {
    array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,nil];
}

//remove the right bar button for our transition
[currentViewController.navigationItem setRightBarButtonItem:nil animated:YES];
//remove the left bar button and create a backbarbutton looking item
//[currentViewController.navigationItem setLeftBarButtonItem:nil animated:NO];

//set the back button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:title style:kButtonStyle target:self action:@selector(goBack)];
[currentViewController.navigationItem setLeftBarButtonItem:backButton animated:YES];
[viewController.navigationItem setLeftBarButtonItem:backButton animated:NO];
[backButton release];

[currentViewController setTitle:viewController.title];

[UIView beginAnimations:@"push view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePushDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[viewController.view setAlpha: 1.0];
[UIView commitAnimations];
}

-(void)animationForCrossFadePushDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{

UIViewController *c = [(NSArray*)context objectAtIndex:0];
UIViewController *n = [(NSArray*)context objectAtIndex:1];
NSString *title     = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:3];
UIBarButtonItem *r = nil;
//not all views have a right bar button, if we look for it and it isn't in the context,
//we'll crash out and not complete the method, but the program won't crash.
//So, we need to check if it is there and skip it if it isn't.
if([(NSArray *)context count] == 5)
    r = [(NSArray *)context objectAtIndex:4];

//Take the new view away from being a subview of the current view so when we go back to it
//it won't be there anymore.
[[[c.view subviews] lastObject] removeFromSuperview];
[c setTitle:title];
[title release];
//set the search button
[c.navigationItem setLeftBarButtonItem:l animated:NO];
//set the next button
if(r != nil)
    [c.navigationItem setRightBarButtonItem:r animated:NO];


[super pushViewController:n animated:NO];

 }

코드에서 언급했듯이 항상 왼쪽 막대 버튼 항목이 있으므로 애니메이션 대리자의 컨텍스트로 전달하는 배열에 배치하기 전에 그것이 없는지 확인하지 않습니다. 이 작업을 수행하면 확인을 원할 수 있습니다.

내가 찾은 문제는 대리자 메서드에서 전혀 충돌하면 프로그램이 충돌하지 않는다는 것입니다. 델리게이트가 완료되는 것을 막지 만 경고는 표시되지 않습니다.
따라서 해당 델리게이트 루틴에서 정리를 수행 한 이후 정리가 완료되지 않아 이상한 시각적 동작이 발생했습니다.

내가 만든 뒤로 버튼은 "goBack"메소드를 호출하고 해당 메소드는 팝 루틴을 호출합니다.

-(void)goBack
{ 
     [self popViewControllerAnimated:YES];
}

또한, 여기 내 팝 루틴이 있습니다.

-(UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    //get the count for the number of viewControllers on the stack
int viewCount = [[self viewControllers] count];
//get the top view controller on the stack
UIViewController *topViewController = [self.viewControllers objectAtIndex:viewCount - 1];
//get the next viewController after the top one (this will be the new top one)
UIViewController *newTopViewController = [self.viewControllers objectAtIndex:viewCount - 2];

//if no animation was requested, we can skip the cross fade
if(!animated)
{
    [super popViewControllerAnimated:NO];
            return topViewController;
}



//start of the cross fade pop.  A bit tricky.  We need to add the new top controller
//as a subview of the curent view controler with an alpha of 0.  We then do a cross fade.
//After that we pop the view controller off the stack without animating it.
//Then the cleanup happens: if the view that was popped is not released, then we
//need to remove the subview we added and change some titles back.
newTopViewController.view.alpha = 0.0;
[topViewController.view addSubview:newTopViewController.view];
[topViewController.view bringSubviewToFront:newTopViewController.view];
NSString *title = [topViewController.title retain];
UIBarButtonItem *lButtonItem = topViewController.navigationItem.leftBarButtonItem;
UIBarButtonItem *rButtonItem = topViewController.navigationItem.rightBarButtonItem;

//set the new buttons on top of the current controller from the new top controller
if(newTopViewController.navigationItem.leftBarButtonItem != nil)
{
    [topViewController.navigationItem setLeftBarButtonItem:newTopViewController.navigationItem.leftBarButtonItem animated:YES];
}
if(newTopViewController.navigationItem.rightBarButtonItem != nil)
{
    [topViewController.navigationItem setRightBarButtonItem:newTopViewController.navigationItem.rightBarButtonItem animated:YES];
}

[topViewController setTitle:newTopViewController.title];
//[topViewController.navigationItem.leftBarButtonItem setTitle:newTopViewController.navigationItem.leftBarButtonItem.title];

NSArray *array = nil;
if(rButtonItem != nil)
    array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,rButtonItem,nil];
else {
    array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,nil];
}


[UIView beginAnimations:@"pop view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePopDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[newTopViewController.view setAlpha: 1.0];
[UIView commitAnimations];
return topViewController;

 }

 -(void)animationForCrossFadePopDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
 {

UIViewController *c = [(NSArray *)context objectAtIndex:0];
//UIViewController *n = [(NSArray *)context objectAtIndex:1];
NSString *title = [(NSArray *)context objectAtIndex:1];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *r = nil;



//Not all views have a right bar button.  If we look for one that isn't there
// we'll crash out and not complete this method, but the program will continue.
//So we need to check if it is therea nd skip it if it isn't.
if([(NSArray *)context count] == 4)
    r = [(NSArray *)context objectAtIndex:3];

//pop the current view from the stack without animation
[super popViewControllerAnimated:NO];

//if what was the current veiw controller is not nil, then lets correct the changes
//we made to it.
if(c != nil)
{
    //remove the subview we added for the transition
    [[c.view.subviews lastObject] removeFromSuperview];
    //reset the title we changed
    c.title = title;
    [title release];
    //replace the left bar button that we changed
    [c.navigationItem setLeftBarButtonItem:l animated:NO];
    //if we were passed a right bar button item, replace that one as well
    if(r != nil)
        [c.navigationItem setRightBarButtonItem:r animated:NO];
    else {
        [c.navigationItem setRightBarButtonItem:nil animated:NO];
    }


 }
}

거의 다됐다. 회전을 구현하려면 추가 코드가 필요합니다. 하위 뷰로 표시하기 전에 뷰를 추가하기 전에 뷰의 프레임 크기를 설정해야합니다. 따라서 하위 뷰로 추가하고 페이드 인하지만 세로로 표시됩니다. 애니메이션없이 팝업하면 동일한 뷰이지만 스택에있는 뷰는 이제 가로입니다. 모든 것이 약간 펑키 해 보입니다. 모든 사람의 회전 구현은 약간 다르므로 여기에 내 코드를 포함시키지 않았습니다.

그것이 사람들에게 도움이되기를 바랍니다. 나는 이것과 비슷한 것을 온통 찾았고 아무것도 찾을 수 없었습니다. 나는 이것이 완벽한 대답이라고 생각하지는 않지만이 시점에서 실제로 잘 작동하고 있습니다.


감탄할 만하지 만, 이것은 솔직히 지금 7 년 후의 해결책이 아닙니다!
Fattie

네 말이 맞아 이 답변은 2011 년부터 왔습니다. 당시에는 효과가 있었지만 그 이후로 많은 변화가있었습니다. =)
georryan

4

이제 사용할 수 있습니다 UIView.transition. 참고하십시오 animated:false. 이것은 모든 전환 옵션, 팝, 푸시 또는 스택 교체와 함께 작동합니다.

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}


3

iJordan의 답변을 영감으로 사용하여 UINavigationController에서 카테고리를 만들어서이 애니메이션 코드를 복사 / 붙여 넣기 대신 앱 전체에서 사용할 수있는 이유는 무엇입니까?

UINavigationController + Animation.h

@interface UINavigationController (Animation)

- (void) pushViewControllerWithFlip:(UIViewController*) controller;

- (void) popViewControllerWithFlip;

@end

UINavigationController + Animation.m

@implementation UINavigationController (Animation)

- (void) pushViewControllerWithFlip:(UIViewController *) controller
{
    [UIView animateWithDuration:0.50
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [self pushViewController:controller animated:NO];
                         [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
                     }];
}

- (void) popViewControllerWithFlip
{
    [UIView animateWithDuration:0.5
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
                     }];

    [self popViewControllerAnimated:NO];
}

@end

그런 다음 UINavigationController + Animation.h 파일을 가져 와서 정상적으로 호출하십시오.

[self.navigationController pushViewControllerWithFlip:[[NewViewController alloc] init]];

[self.navigationController popViewControllerWithFlip];

영리한. 그러나 flipFromRight에 하드 코드가 아닌 UIViewAnimationTransition의 인수를 취하는 push / pop 메소드를 추가하지 않는 이유는 무엇입니까?
Jef

@Jef 이것들은 편리한 메소드입니다-이런 식으로, 구현자는 각 특정 애니메이션 유형에 대해 어떤 UIViewAnimationTransition 값을 전달해야하는지 기억할 필요가 없습니다. 그들은 달성하고자하는 "영어"이름으로 메소드를 호출하기 만합니다.
DiscDev

@Jef, 당신의 제안은 확실히 유효합니다-objective-c를 계속 사용하고 많은 전환 스타일을 지원 해야하는 경우 (많은 전환 스타일이 사용자를 혼동하기 때문에 확실히 권장되지 않습니다) UIViewAnimationTransition 유형을 취하는 1 가지 방법이 있습니다. 개발을 더 쉽게하기위한 몇 가지 편리한 방법.
DiscDev

3

매우 간단합니다

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft

StackOverflow에 오신 것을 환영합니다. 코드, XML 또는 데이터 샘플을 게시하는 경우 텍스트 편집기에서 해당 행을 강조 표시하고 편집기 도구 모음에서 "코드 샘플"버튼 ({})을 클릭하거나 키보드에서 Ctrl + K를 사용하여 형식을 지정하십시오. 그리고 구문 강조!
WhatsThePoint

2

Applidium 에서 생성 한 사용자 지정 전환 애니메이션 (API는 UINavigationController의 API와 일치)으로 UINavigationController를 대체하는 ADTransitionController를 살펴보십시오.

스 와이프 , 페이드 , 큐브 , 회전 목마 , 등과 같은 푸시 액션에 서로 다른 사전 정의 된 애니메이션을 사용할 수 있습니다 .


2

여기에있는 모든 대답은 훌륭하고 대부분 잘 작동하지만 동일한 효과를 얻는 약간 더 간단한 방법이 있습니다 ...

푸시의 경우 :

  NextViewController *nextViewController = [[NextViewController alloc] init];

  // Shift the view to take the status bar into account 
  CGRect frame = nextViewController.view.frame;
  frame.origin.y -= 20;
  frame.size.height += 20;
  nextViewController.view.frame = frame;

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextViewController.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) {
    [self.navigationController pushViewController:nextViewController animated:NO];
  }];

팝의 경우 :

  int numViewControllers = self.navigationController.viewControllers.count;
  UIView *nextView = [[self.navigationController.viewControllers objectAtIndex:numViewControllers - 2] view];

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextView duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
    [self.navigationController popViewControllerAnimated:NO];
  }];}

루트 뷰 컨트롤러에 팝업하면 충돌이 발생합니다.
압둘라 우 메르

1

전환 애니메이션을 공개적으로 변경할 수있는 방법은 없습니다.

"뒤로"버튼이 필요하지 않은 경우 당신이 해야 사용 모달 뷰 컨트롤러 / "페이드"/ (≥3.2) "페이지 컬"전환 / "플립"는 "바닥에서 밀어 넣기"를 가지고.


개인 측면, 방법은 -pushViewController:animated:문서화되지 않은 메서드를 호출 -pushViewController:transition:forceImmediate:예를 들면 있도록 왼쪽 플립부터 오른쪽으로의 전환, 당신이 사용할 수있는 것인지,

[navCtrler pushViewController:ctrler transition:10 forceImmediate:NO];

그러나 "팝"전환은이 방법으로 변경할 수 없습니다.


1

훨씬 적은 수의 코드 줄로 처리하는 방법 은이 질문에 대한 나의 답변 참조하십시오 . 이 방법을 사용하면 원하는 방식으로 새 뷰 컨트롤러의 의사 "푸시"에 애니메이션을 적용 할 수 있으며 애니메이션이 완료되면 마치 표준 푸시 방법을 사용한 것처럼 내비게이션 컨트롤러가 설정됩니다. 내 예제에서는 왼쪽 또는 오른쪽에서 슬라이드 인을 애니메이션 할 수 있습니다. 편의를 위해 여기에 코드를 반복했습니다.

-(void) showVC:(UIViewController *) nextVC rightToLeft:(BOOL) rightToLeft {
    [self addChildViewController:neighbor];
    CGRect offscreenFrame = self.view.frame;
    if(rightToLeft) {
        offscreenFrame.origin.x = offscreenFrame.size.width * -1.0;
    } else if(direction == MyClimbDirectionRight) {
        offscreenFrame.origin.x = offscreenFrame.size.width;
    }
    [[neighbor view] setFrame:offscreenFrame];
    [self.view addSubview:[neighbor view]];
    [neighbor didMoveToParentViewController:self];
    [UIView animateWithDuration:0.5 animations:^{
        [[neighbor view] setFrame:self.view.frame];
    } completion:^(BOOL finished){
        [neighbor willMoveToParentViewController:nil];
        [neighbor.view removeFromSuperview];
        [neighbor removeFromParentViewController];
        [[self navigationController] pushViewController:neighbor animated:NO];
        NSMutableArray *newStack = [[[self navigationController] viewControllers] mutableCopy];
        [newStack removeObjectAtIndex:1]; //self, just below top
        [[self navigationController] setViewControllers:newStack];
    }];
}

0

샘플 앱에서이 변형을 확인하십시오. https://github.com/mpospese/MPFoldTransition/

#pragma mark - UINavigationController(MPFoldTransition)

@implementation UINavigationController(MPFoldTransition)

//- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
- (void)pushViewController:(UIViewController *)viewController foldStyle:(MPFoldStyle)style
{
    [MPFoldTransition transitionFromViewController:[self visibleViewController] 
                                  toViewController:viewController 
                                          duration:[MPFoldTransition defaultDuration]  
                                             style:style 
                                        completion:^(BOOL finished) {
                                            [self pushViewController:viewController animated:NO];
                                        }
     ];
}

- (UIViewController *)popViewControllerWithFoldStyle:(MPFoldStyle)style
{
    UIViewController *toController = [[self viewControllers] objectAtIndex:[[self viewControllers] count] - 2];

    [MPFoldTransition transitionFromViewController:[self visibleViewController] 
                                  toViewController:toController 
                                          duration:[MPFoldTransition defaultDuration] 
                                             style:style
                                        completion:^(BOOL finished) {
                                            [self popViewControllerAnimated:NO];
                                        }
     ];

    return toController;
}

0

그냥 사용하십시오 :

ViewController *viewController = [[ViewController alloc] init];

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBarHidden = YES;

[self presentViewController:navController animated:YES completion: nil];
[viewController release];
[navController release];

0

이것은 오래된 질문입니다. viewControllers제안 된 답변으로 몇 가지 를 채우는 데 문제가 있었으므로 여전히이 답변을 게시하고 싶습니다 . 내 솔루션은 UINavigationController모든 pop 및 push 메소드 를 서브 클래스 화 하고 재정의하는 것입니다.

FlippingNavigationController.h

@interface FlippingNavigationController : UINavigationController

@end

FlippingNavigationController.m :

#import "FlippingNavigationController.h"

#define FLIP_DURATION 0.5

@implementation FlippingNavigationController

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [UIView transitionWithView:self.view
                      duration:animated?FLIP_DURATION:0
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromRight
                    animations:^{ [super pushViewController:viewController
                                                   animated:NO]; }
                    completion:nil];
}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    return [[self popToViewController:[self.viewControllers[self.viewControllers.count - 2]]
                             animated:animated] lastObject];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
    return [self popToViewController:[self.viewControllers firstObject]
                            animated:animated];
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    __block NSArray* viewControllers = nil;

    [UIView transitionWithView:self.view
                      duration:animated?FLIP_DURATION:0
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromLeft
                    animations:^{ viewControllers = [super popToViewController:viewController animated:NO]; }
                    completion:nil];

    return viewControllers;
}

@end

0

나는이 실이 오래되었다는 것을 알고 있지만, 나는 2 센트를 넣을 것이라고 생각했다. 커스텀 애니메이션을 만들 필요는 없습니다. 간단하고 해킹이있는 방법이 있습니다. push를 사용하는 대신 새 탐색 컨트롤러를 만들고 새보기 컨트롤러를 해당 탐색 컨트롤러의 루트보기 컨트롤러로 만든 다음 원래 탐색 컨트롤러에서 탐색 컨트롤러를 표시합니다. 현재는 다양한 스타일로 쉽게 사용자 정의 할 수 있으며 사용자 정의 애니메이션을 만들 필요가 없습니다.

예를 들면 다음과 같습니다.

UIViewcontroller viewControllerYouWantToPush = UIViewController()
UINavigationController newNavController = UINavigationController(root: viewControllerYouWantToView)
newNavController.navBarHidden = YES;
self.navigationController.present(newNavController)

원하는 프리젠 테이션 스타일을 변경할 수 있습니다.


-1

내 목적에 맞는이 작업을 수행하는 온화한 재귀 방법을 찾았습니다. 일반 팝업 애니메이션을 차단하고 애니메이션이 아닌 팝업 메시지를 대체하는 데 사용하는 인스턴스 변수 BOOL이 있습니다. 변수는 처음에 NO로 설정됩니다. 뒤로 버튼을 누르면 델리게이트 메소드가이를 YES로 설정하고 애니메이션되지 않은 새 팝업 메시지를 탐색 모음에 전송하여 이번에는 변수를 YES로 설정하여 동일한 델리게이트 메소드를 다시 호출합니다. 변수가 YES로 설정된 경우 대리자 메서드는 변수를 NO로 설정하고 애니메이션이없는 팝이 발생하도록 YES를 반환합니다. 두 번째 델리게이트 호출이 반환 된 후 첫 번째 델리게이트 호출로 돌아 가면 NO가 반환되어 원래 애니메이션 팝이 차단됩니다. 실제로 들리는 것만 큼 지저분하지는 않습니다. 내 shouldPopItem 메소드는 다음과 같습니다.

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item 
{
    if ([[navigationBar items] indexOfObject:item] == 1) 
    {
        [expandedStack restack];    
    }

    if (!progPop) 
    {
        progPop = YES;
        [navBar popNavigationItemAnimated:NO];
        return NO;
    }
    else 
    {
        progPop = NO;
        return YES;
    }
}

나를 위해 작동합니다.

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