Cách thay đổi hoạt ảnh Đẩy và Pop trong ứng dụng dựa trên điều hướng


221

Tôi có một ứng dụng dựa trên điều hướng và tôi muốn thay đổi hình ảnh động của hoạt hình đẩy và pop. Làm thế nào tôi có thể làm điều đó?

Chỉnh sửa 2018

Đã có nhiều câu trả lời cho câu hỏi này và bây giờ đã khá lâu rồi, tôi đã chọn lại câu trả lời cho những gì tôi tin là phù hợp nhất bây giờ. Nếu có ai đó nghĩ khác, xin vui lòng cho tôi biết trong phần bình luận


25
Kể từ iOS 7, có API chính thức cho việc này; xem hỗ trợ hoạt hình chuyển tiếp tùy chỉnh của UINavestionControllDelegate . Ngoài ra còn có một video WWDC 2013 về điều này.
Jesse Rusak

Tôi đã thêm một câu trả lời (bên dưới) để thực hiện điều này trong Swift - Tôi đã gặp câu hỏi này về việc triển khai Swift vì vậy tôi nghĩ rằng tôi sẽ đồng ý với việc triển khai tiếp theo.
djbp

1
Để có hướng dẫn rất tốt với API chính thức (iOS 7+), hãy xem: bradbambara.wordpress.com/2014/04/11/ mẹo
nikolovski

1
@JesseRusak đã cập nhật liên kết tới WWDC 2013 Video: developer.apple.com/ideo/play/wwdc2013-218
Wojciech Rutkowski

1
Thay đổi câu trả lời chấp nhận của tôi kẻ n gals. Hi vọng điêu nay co ich! GLHF
Jab

Câu trả lời:


35

Cách thay đổi hoạt ảnh Đẩy và Pop trong ứng dụng dựa trên điều hướng ...

Đối với năm 2019, "câu trả lời cuối cùng!"

Lời mở đầu:

Giả sử bạn là người mới phát triển iOS. Một cách khó hiểu, Apple cung cấp hai chuyển đổi có thể được sử dụng dễ dàng. Đó là: "crossfade" và "flip".

Nhưng tất nhiên, "crossfade" và "flip" là vô dụng. Chúng không bao giờ được sử dụng. Không ai biết tại sao Apple cung cấp hai quá trình chuyển đổi vô dụng đó!

Vì thế:

Giả sử bạn muốn thực hiện một chuyển đổi thông thường, phổ biến, chẳng hạn như "slide". Trong trường hợp đó, bạn phải làm rất nhiều việc! .

Công việc đó, được giải thích trong bài này.

Chỉ cần lặp lại:

Đáng ngạc nhiên: với iOS, nếu bạn muốn chuyển đổi hàng ngày đơn giản nhất, phổ biến nhất (như một slide thông thường), bạn phải thực hiện tất cả các công việc thực hiện chuyển đổi tùy chỉnh đầy đủ .

Đây là cách để làm điều đó ...

1. Bạn cần một tùy chỉnh UIViewControllerAnimatedTransitioning

  1. Bạn cần một bool của riêng bạn như thế nào popStyle. (Có phải nó bật lên, hoặc bật ra không?)

  2. Bạn phải bao gồm transitionDuration(tầm thường) và cuộc gọi chính,animateTransition

  3. Trong thực tế, bạn phải viết hai thói quen khác nhau cho bên trong animateTransition. Một cho đẩy, và một cho pop. Có lẽ đặt tên cho chúng animatePushanimatePop. Bên trong animateTransition, chỉ cần phân nhánh vào popStylehai thói quen

  4. Ví dụ dưới đây thực hiện di chuyển đơn giản / di chuyển tắt

  5. Trong animatePushanimatePopthói quen của bạn . Bạn phải có được "từ chế độ xem" và "để xem". (Cách làm điều đó, được hiển thị trong ví dụ mã.)

  6. và bạn phải addSubview cho chế độ xem "đến" mới.

  7. và bạn phải gọi completeTransitionvào cuối anime của bạn

Vì thế ..

  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)
            })
        }
    }

Và sau đó ...

2. Sử dụng nó trong bộ điều khiển xem của bạn.

Lưu ý: thật kỳ lạ, bạn chỉ phải làm điều này trong trình điều khiển xem "đầu tiên". (Cái "bên dưới".)

Với cái mà bạn bật lên trên , không làm gì cả . Dễ dàng.

Vì vậy, lớp của bạn ...

class SomeScreen: UIViewController {
}

trở thành ...

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

Đó là nó.

Đẩy và bật chính xác như bình thường, không thay đổi. Đẩy ...

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

và để bật nó, bạn có thể nếu bạn muốn làm điều đó trên màn hình tiếp theo:

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

Phù.


3. Cũng thích các câu trả lời khác trên trang này giải thích cách ghi đè AnimatedTransitioning

Di chuyển đến @AlanZeino và @elias trả lời để thảo luận thêm về cách AnimatedTransitioningvào ứng dụng iOS trong những ngày này!


Thông minh! Nếu tôi muốn cử chỉ vuốt ngược điều hướng cũng hỗ trợ AnimatedTransitioning tương tự. Bất kỳ ý tưởng?
sam chi wen

cảm ơn @samchiwen - thực sự đó chính xác là những gì animatePushanimatePoplà .. hai hướng khác nhau!
Fattie

268

Tôi đã làm như sau và nó hoạt động tốt .. và đơn giản và dễ hiểu ..

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

Và điều tương tự cho đẩy ..


Phiên bản Swift 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, đây thực sự là giải pháp lành mạnh nhất. Chỉ cần một lưu ý nhỏ cho khách truy cập trong tương lai: Animated:NOphần quan trọng. Nếu YESđược thông qua, các hình ảnh động trộn lẫn và gây ra hiệu ứng hài hước.
DarkDust

12
Giải pháp tốt nhất cho đến nay .. Và đối với người mới bắt đầu, đừng quên bao gồm QuartCore (#import <QuartzCore / QuartzCore.h>)
nomann

4
Vấn đề duy nhất tôi gặp phải với giải pháp này là viewDidAppear của trình điều khiển đẩy được gọi ngay lập tức sau khi tôi đẩy nó mà không có hình ảnh động. Có cách nào xung quanh nó?
Pedro Mancheno

9
Vấn đề của tôi với mã này là mỗi chế độ xem dường như chuyển sang màu xám hoặc trắng khi chúng trượt vào hoặc ra.
Chris

1
đã kiểm tra trên iOS 7.1.2 và iOS 8.3 - mã này đang hoạt động tốt cũng hoạt động tốt cho phương thứcsetViewControllers:
proff

256

Đây là cách tôi luôn quản lý để hoàn thành nhiệm vụ này.

Để đẩy:

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

Dành cho nhạc Pop:

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


Tôi vẫn nhận được rất nhiều phản hồi từ việc này vì vậy tôi sẽ tiếp tục và cập nhật nó để sử dụng các khối hoạt hình, đây là cách được Apple khuyến nghị để làm hoạt hình.

Để đẩy:

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

Dành cho nhạc Pop:

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

3
Cảm ơn vì điều đó. Nhưng pop được thực hiện tự động bởi UINavestionControll. Làm thế nào để bạn ghi đè hành vi đó để bạn có thể gọi logic pop tùy chỉnh của mình?
Joshua Frank

1
@stuckj thực sự nó hoạt động !!! bạn chỉ cần thay thế superbằngself.navigationController
holierthanthou84

Có cách nào để có một slide từ bên trái thay vì slide mặc định từ bên phải không?
shim

Cái đầu tiên không hiển thị cái nhìn mới nào cả. Thứ hai không hiển thị hình ảnh động. Câu trả lời rất tệ! iOS 7.
Dmitry

2
tại sao bạn lại cho UIViewControllerlớp con một tên mà không có phần "ViewContoder". Tên này phù hợp hơn cho UIView.
dùng2159978

29

để đẩy

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

cho nhạc pop

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 trả lời, chỉ sau đó cho 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)

Một số sidenote:

Bạn có thể làm điều này tốt với Segue, chỉ cần thực hiện điều này trong prepareForSeguehoặc shouldPerformSegueWithIdentifier. Tuy nhiên , điều này cũng sẽ giữ hình ảnh động mặc định trong đó. Để khắc phục điều này, bạn phải vào bảng phân cảnh, nhấp vào Phân đoạn và bỏ chọn hộp 'Hoạt hình'. Nhưng điều này sẽ giới hạn ứng dụng của bạn cho iOS 9.0 trở lên (ít nhất là khi tôi đã làm nó trong Xcode 7).

Khi thực hiện trong một segue, hai dòng cuối cùng nên được thay thế bằng:

self.navigationController?.popViewControllerAnimated(false)

Mặc dù tôi đặt sai, nhưng nó bỏ qua nó.


Làm thế nào để loại bỏ màu đen trong nền ở cuối hoạt hình.
Madhu

Không hoạt động đối với hoạt hình của trình điều khiển chế độ xem hoạt động đối với trình điều khiển chế độ xem Pop
Mukul Thêm

16

Hãy nhớ rằng trong Swift , tiện ích mở rộng chắc chắn là bạn của bạn!

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

Sử dụng các cuộc gọi riêng tư là một ý tưởng tồi vì Apple không còn phê duyệt các ứng dụng làm điều đó. Có lẽ bạn có thể thử điều này:

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

Nó chỉ hoạt động "một nửa" - nó sẽ không giải quyết được vấn đề khó hơn của hoạt hình pop.
Adam

Tôi thích giải pháp này tốt hơn, và vâng nó hoạt động. Sử dụng phương pháp riêng tư sẽ khiến bạn bị từ chối chắc chắn.
Benjamin Intal

@nicktmro đó là cuộc gọi api riêng. Tôi đã không nhận thấy bất kỳ.
Franklin

@Franklin đã có một cuộc thảo luận ở đây một thời gian trước đây xung quanh việc sử dụng -pushViewController:transition:forceImmediate:đó sẽ là một ý tưởng tồi.
nicktmro

9

Vì đây là kết quả hàng đầu trên Google, tôi nghĩ tôi muốn chia sẻ những gì tôi nghĩ là cách lành mạnh nhất; đó là sử dụng API chuyển tiếp iOS 7+. Tôi đã triển khai điều này cho iOS 10 với Swift 3.

Thật đơn giản để kết hợp điều này với cách hoạt UINavigationControllerhình giữa hai bộ điều khiển khung nhìn nếu bạn tạo một lớp con UINavigationControllervà trả về một thể hiện của một lớp phù hợp với UIViewControllerAnimatedTransitioninggiao thức.

Ví dụ ở đây là UINavigationControllerlớp con của tôi :

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)
    }

}

Bạn có thể thấy rằng tôi đặt UINavigationControllerDelegatechính nó và trong một phần mở rộng trên lớp con của tôi, tôi triển khai phương thức trong UINavigationControllerDelegateđó cho phép bạn trả về bộ điều khiển hoạt hình tùy chỉnh (nghĩa là NavigationControllerAnimation). Bộ điều khiển hoạt hình tùy chỉnh này sẽ thay thế hình ảnh động cho bạn.

Có lẽ bạn đang tự hỏi tại sao tôi chuyển hoạt động sang NavigationControllerAnimationthể hiện thông qua trình khởi tạo của nó. Tôi làm điều này để trong NavigationControllerAnimationquá trình thực hiện UIViewControllerAnimatedTransitioninggiao thức, tôi biết thao tác là gì (nghĩa là 'đẩy' hoặc 'pop'). Điều này giúp biết loại hoạt hình nào tôi nên làm. Hầu hết thời gian, bạn muốn thực hiện một hình ảnh động khác nhau tùy thuộc vào hoạt động.

Phần còn lại là khá chuẩn. Thực hiện hai chức năng cần thiết trong UIViewControllerAnimatedTransitioninggiao thức và hoạt hình theo cách bạn muốn:

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

Điều quan trọng cần nhớ là, đối với mỗi loại hoạt động khác nhau (nghĩa là 'đẩy' hoặc 'pop), bộ điều khiển xem và từ sẽ khác nhau. Khi bạn đang ở trong một hoạt động đẩy, bộ điều khiển để xem sẽ là bộ điều khiển được đẩy. Khi bạn đang ở trong một hoạt động pop, bộ điều khiển để xem sẽ là bộ điều khiển đang được chuyển sang và bộ điều khiển từ chế độ xem sẽ là bộ điều khiển được bật.

Ngoài ra, tobộ điều khiển xem phải được thêm dưới dạng một khung nhìn phụ containerViewtrong bối cảnh chuyển tiếp.

Khi hoạt hình của bạn hoàn thành, bạn phải gọi transitionContext.completeTransition(true). Nếu bạn đang làm một sự chuyển tiếp tương tác, bạn sẽ phải tự động trả về một Booltới completeTransition(didComplete: Bool), tùy thuộc vào nếu quá trình chuyển đổi hoàn tất vào cuối của phim hoạt hình.

Cuối cùng ( đọc tùy chọn ), bạn có thể muốn xem tôi đã thực hiện quá trình chuyển đổi như thế nào. Mã này hơi khó sử dụng hơn một chút và tôi đã viết nó khá nhanh vì vậy tôi sẽ không nói đó là mã hoạt hình tuyệt vời nhưng nó vẫn chỉ ra cách thực hiện phần hoạt hình.

Của tôi là một quá trình chuyển đổi thực sự đơn giản; Tôi muốn bắt chước hoạt hình tương tự như UINavestionControll thường làm, nhưng thay vì hoạt hình 'trang tiếp theo trên đầu trang', tôi muốn triển khai hoạt hình 1: 1 của trình điều khiển chế độ xem cũ cùng lúc với chế độ xem mới bộ điều khiển xuất hiện. Điều này có tác dụng làm cho hai bộ điều khiển khung nhìn có vẻ như chúng được ghim vào nhau.

Đối với thao tác đẩy, trước tiên, yêu cầu thiết lập toViewControllernguồn gốc chế độ xem trên màn hình tắt trục x, thêm nó vào chế độ xem phụ containerView, tạo hiệu ứng trên màn hình bằng cách đặt giá trị đó origin.xthành 0. Đồng thời, tôi làm nổi bật fromViewControllertầm nhìn của mình bằng cách origin.xtắt màn hình:

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)
})

Các hoạt động pop về cơ bản là nghịch đảo. Thêm phần toViewControllerdưới dạng một khung nhìn phụ containerViewvà tạo hiệu fromViewControllerứng từ bên phải khi bạn tạo hình động toViewControllertừ bên trái:

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)
})

Đây là một ý chính với toàn bộ tập tin nhanh chóng:

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


Tuyệt quá!. Những gì tôi muốn làm là CHỈ để hoạt hình theo hướng ngược lại. Tôi đã kiểm tra một số giải pháp khác, nhưng tất cả các cuộc triển lãm nhấp nháy ở màn hình bên trái và bên phải. Có vẻ như hoạt hình thay đổi alpha ngầm định không thể được gỡ bỏ với chúng. Chỉ có giải pháp này khắc phục vấn đề.
beshio

vâng, đây là giải pháp hiện đại đúng duy nhất. (Tôi không phiền, nhưng nó chính xác giống như giải pháp tôi đã gõ bên dưới! :))
Fattie 17/03/18

@AlanZeino Điều gì xảy ra nếu bên trong cùng một ViewContoder bạn cần phải có Ảnh động khác nhau cho nhấp vào nút khác nhau? Vì vậy, đối với button1 bạn cần một hình ảnh động hòa tan, đối với button2 bạn cần chuyển đổi mặc định.
jzeferino

7

Có UINavestionControllDelegate và UIViewControllAnimatedTransitioning ở đó bạn có thể thay đổi hình ảnh động cho bất cứ điều gì bạn muốn.

Ví dụ: đây là hoạt hình pop dọc cho 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())
    })
}

}

Và sau đó

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

Hướng dẫn hữu ích https://www.objc.io/issues/5-ios7/view-controll-transitions/


6

Dựa trên câu trả lời được cập nhật cho swift 4jordanperry

Để đẩy 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)
})

Dành cho nhạc pop

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

Đây là cách tôi đã làm tương tự trong Swift:

Để đẩy:

    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)
    })

Dành cho nhạc Pop:

Tôi thực sự đã làm điều này hơi khác với một số câu trả lời ở trên - nhưng vì tôi là người mới phát triển Swift, nên nó có thể không đúng. Tôi đã ghi đè viewWillDisappear:animated:và thêm mã pop trong đó:

    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

Câu trả lời của @Luca Davanzo trong Swift 4.2

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

Gần đây tôi đã cố gắng làm một cái gì đó tương tự. Tôi đã quyết định tôi không thích hoạt hình trượt của UINavestionControll, nhưng tôi cũng không muốn thực hiện các hoạt hình mà UIView cung cấp cho bạn như curl hoặc bất cứ thứ gì tương tự. Tôi muốn làm mờ dần giữa các lượt xem khi tôi đẩy hoặc bật.

Vấn đề ở đây liên quan đến thực tế là chế độ xem thực sự loại bỏ chế độ xem hoặc xuất hiện trên chế độ xem hiện tại, do đó, một hiệu ứng mờ dần không hoạt động. Giải pháp tôi đưa ra là tham gia vào chế độ xem mới của tôi và thêm nó dưới dạng một khung nhìn phụ vào chế độ xem hàng đầu hiện tại trên ngăn xếp của UIViewContoder. Tôi thêm nó với hệ số alpha bằng 0, sau đó thực hiện xen kẽ. Khi chuỗi hoạt hình kết thúc, tôi đẩy khung nhìn lên ngăn xếp mà không làm động nó. Sau đó tôi quay lại topView cũ và dọn dẹp những thứ mà tôi đã thay đổi.

Nó phức tạp hơn một chút so với điều đó, bởi vì bạn có điều hướng. Bạn phải điều chỉnh để làm cho quá trình chuyển đổi trông chính xác. Ngoài ra, nếu bạn thực hiện bất kỳ thao tác xoay nào, thì bạn phải điều chỉnh kích thước khung hình khi bạn thêm chế độ xem dưới dạng xem trước để chúng hiển thị chính xác trên màn hình. Đây là một số mã tôi đã sử dụng. Tôi đã phân lớp UINavestionControll và vượt qua các phương thức đẩy và 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];

 }

Như tôi đã đề cập trong mã, tôi luôn có một mục nút thanh bên trái, vì vậy tôi không kiểm tra xem nó có phải không trước khi đặt nó vào mảng mà tôi chuyển qua làm bối cảnh cho đại biểu hoạt hình. Nếu bạn làm điều này, bạn có thể muốn thực hiện kiểm tra đó.

Vấn đề tôi tìm thấy là nếu bạn gặp sự cố trong phương thức ủy nhiệm, nó sẽ không làm hỏng chương trình. Nó chỉ ngăn đại biểu hoàn thành nhưng bạn không nhận được bất kỳ cảnh báo nào.
Vì vậy, vì tôi đang thực hiện công việc dọn dẹp của mình trong thói quen đại biểu đó, nó đã gây ra một số hành vi thị giác kỳ lạ vì nó không hoàn thành việc dọn dẹp.

Nút quay lại mà tôi tạo gọi là phương thức "goBack" và phương thức đó chỉ gọi thói quen pop.

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

Ngoài ra, đây là thói quen pop của tôi.

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


 }
}

Nó khá là nhiều. Bạn sẽ cần một số mã bổ sung nếu bạn muốn thực hiện các phép quay. Bạn sẽ cần đặt kích thước khung hình cho các chế độ xem mà bạn thêm dưới dạng các lần xem trước khi bạn hiển thị chúng nếu không bạn sẽ gặp phải các vấn đề về hướng là phong cảnh, nhưng lần cuối cùng bạn nhìn thấy chế độ xem trước đó là chế độ xem dọc. Vì vậy, sau đó bạn thêm nó dưới dạng chế độ xem phụ và làm mờ nó trong nhưng nó hiển thị dưới dạng chân dung, sau đó khi chúng tôi bật mà không có hình động, cùng một chế độ xem, nhưng chế độ xem trong ngăn xếp, bây giờ là phong cảnh. Toàn bộ điều này có vẻ một chút sôi nổi. Việc thực hiện xoay vòng của mọi người là một chút khác nhau vì vậy tôi đã không bao gồm mã của mình cho điều đó ở đây.

Hy vọng nó sẽ giúp một số người. Tôi đã tìm kiếm tất cả những thứ như thế này và không thể tìm thấy bất cứ điều gì. Tôi không nghĩ rằng đây là câu trả lời hoàn hảo, nhưng nó đang hoạt động rất tốt với tôi vào thời điểm này.


Trong khi đáng ngưỡng mộ, đây thực sự không phải là giải pháp bây giờ 7 năm sau!
Fattie

Bạn đúng rồi. Câu trả lời này là từ năm 2011. Nó đã hoạt động trở lại, nhưng mọi thứ đã thay đổi rất nhiều kể từ đó. =)
geiltyan

4

Bây giờ bạn có thể sử dụng UIView.transition. Lưu ý rằng animated:false. Điều này hoạt động với bất kỳ tùy chọn chuyển tiếp, bật, đẩy hoặc thay thế ngăn xếp.

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

1
@Fattie, phương pháp đặc biệt này chỉ hoạt động với bất kỳ hình ảnh động tiêu chuẩn nào như flips và curls được liệt kê trong developer.apple.com/documentation/uikit/uiviewanimationoptions
Peter Deweese

3

Sử dụng câu trả lời của iJordan làm nguồn cảm hứng, tại sao không chỉ đơn giản là tạo Danh mục trên UINavestionControll để sử dụng trong toàn bộ ứng dụng của bạn thay vì sao chép / dán mã hoạt hình này ở mọi nơi?

UINavestionControll + Animation.h

@interface UINavigationController (Animation)

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

- (void) popViewControllerWithFlip;

@end

UINavestionControll + 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

Sau đó, chỉ cần nhập tệp UINavestionControll + Animation.h và gọi nó bình thường:

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

[self.navigationController popViewControllerWithFlip];

Tài giỏi. Nhưng tại sao không thêm các phương thức đẩy / pop lấy đối số của UIViewAnimationTransition thay vì mã cứng cho flipFromRight?
Jef

@Jef đây là các phương thức tiện lợi - theo cách này, người triển khai không cần nhớ giá trị UIViewAnimationTransition nào được truyền cho mỗi loại hoạt hình cụ thể, họ chỉ cần gọi phương thức với tên "tiếng Anh" về những gì họ muốn thực hiện.
DiscDev 7/03/2016

@Jef cũng vậy, đề xuất của bạn chắc chắn hợp lệ - nếu tôi vẫn đang sử dụng object-c và cần hỗ trợ nhiều kiểu chuyển đổi (chắc chắn không được đề xuất vì nhiều kiểu chuyển đổi khác nhau sẽ gây nhầm lẫn cho người dùng) Tôi sẽ có 1 phương thức sử dụng loại UIViewAnimationTransition, sau đó một số phương pháp thuận tiện để làm cho việc phát triển dễ dàng hơn.
DiscDev

3

Nó rất đơn giản

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft

Chào mừng bạn đến với StackOverflow: nếu bạn đăng các mẫu mã, XML hoặc dữ liệu, vui lòng tô sáng các dòng đó trong trình soạn thảo văn bản và nhấp vào nút "mẫu mã" ({}) trên thanh công cụ của trình soạn thảo hoặc sử dụng Ctrl + K trên bàn phím để định dạng độc đáo và cú pháp làm nổi bật nó!
WhatsThePoint

2

Hãy xem ADTransitionCont kiểm , một sự thay thế cho UINavestionContaptor với các hình ảnh động chuyển đổi tùy chỉnh (API của nó khớp với API của UINavestionControll) mà chúng tôi đã tạo tại Applidium.

Bạn có thể sử dụng các hình ảnh động được xác định trước khác nhau cho các hành động đẩybật như Swipe , Fade , Cube , Carrousel , Zoom , v.v.


2

Mặc dù tất cả các câu trả lời ở đây đều rất hay và hầu hết đều hoạt động rất tốt, có một phương pháp đơn giản hơn một chút để đạt được hiệu quả tương tự ...

Để đẩy:

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

Dành cho nhạc Pop:

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

Điều này sẽ sụp đổ khi bật đến bộ điều khiển xem gốc.
Abdullah Umer

1

Tôi không biết bất kỳ cách nào bạn có thể thay đổi hình ảnh chuyển tiếp công khai.

Nếu không cần nút "quay lại", bạn nên sử dụng các bộ điều khiển chế độ xem theo chế độ để có các hiệu ứng chuyển tiếp "đẩy từ dưới lên" / "lật" / "mờ" / ((3.2).


Về phía riêng tư , phương thức -pushViewController:animated:gọi phương thức không có giấy tờ -pushViewController:transition:forceImmediate:, vì vậy, ví dụ: nếu bạn muốn chuyển đổi từ trái sang phải sang phải, bạn có thể sử dụng

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

Tuy nhiên, bạn không thể thay đổi quá trình chuyển đổi "pop" theo cách này.


1

Xem câu trả lời của tôi cho câu hỏi này để biết cách thực hiện trong ít dòng mã hơn. Phương pháp này cho phép bạn tạo hiệu ứng giả - "Đẩy" bộ điều khiển chế độ xem mới theo bất kỳ cách nào bạn thích và khi hoạt ảnh hoàn thành, nó sẽ thiết lập Bộ điều khiển Điều hướng giống như bạn đã sử dụng phương pháp Đẩy tiêu chuẩn. Ví dụ của tôi cho phép bạn tạo hiệu ứng trượt từ bên trái hoặc bên phải. Mã được lặp lại ở đây để thuận tiện:

-(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

Từ ứng dụng mẫu, hãy kiểm tra biến thể này. 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

Chỉ dùng:

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

Nhận ra đây là một câu hỏi cũ. Tôi vẫn muốn đăng câu trả lời này, vì tôi có một số vấn đề nảy sinh viewControllersvới một số câu trả lời được đề xuất. Giải pháp của tôi là phân lớp UINavigationControllervà ghi đè tất cả các phương thức pop và đẩy.

FlippingNavlationControll.h

@interface FlippingNavigationController : UINavigationController

@end

FlippingNavlationControll.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

Tôi biết chủ đề này đã cũ, nhưng tôi nghĩ tôi đã bỏ vào hai xu của mình. Bạn không cần phải tạo một hình ảnh động tùy chỉnh, có một cách đơn giản (có thể là hacky) để làm điều đó. Thay vì sử dụng đẩy, hãy tạo bộ điều khiển điều hướng mới, biến bộ điều khiển chế độ xem mới thành bộ điều khiển xem gốc của bộ điều khiển điều hướng đó, sau đó trình bày bộ điều khiển điều hướng từ bộ điều khiển điều hướng gốc. Hiện tại có thể dễ dàng tùy chỉnh với nhiều phong cách, và không cần phải tạo một hình ảnh động tùy chỉnh.

Ví dụ:

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

Và bạn có thể thay đổi phong cách trình bày theo cách bạn muốn.


-1

Tôi tìm thấy một cách đệ quy nhẹ để làm điều này phù hợp với mục đích của tôi. Tôi có một biến BOOL mà tôi sử dụng để chặn hoạt hình popping bình thường và thay thế tin nhắn pop không hoạt hình của riêng tôi. Biến ban đầu được đặt thành NO. Khi nhấn nút quay lại, phương thức ủy nhiệm đặt nó thành CÓ và gửi một thông báo pop không hoạt hình mới đến thanh điều hướng, do đó gọi lại phương thức ủy nhiệm tương tự, lần này với biến được đặt thành CÓ. Với biến được đặt thành CÓ, phương thức ủy nhiệm đặt thành KHÔNG và trả về CÓ để cho phép pop không hoạt hình xảy ra. Sau khi cuộc gọi đại biểu thứ hai trở lại, chúng tôi kết thúc trở lại trong lần đầu tiên, nơi NO được trả lại, chặn cửa sổ hoạt hình gốc! Nó thực sự không lộn xộn như âm thanh. Phương thức ShouldPopItem của tôi trông như thế này:

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

Làm việc cho tôi.

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.