Cách trình bày bộ điều khiển chế độ xem từ phải sang trái trong iOS bằng Swift


82

Tôi đang sử dụng presentViewController để trình bày màn hình mới

let dashboardWorkout = DashboardWorkoutViewController()
presentViewController(dashboardWorkout, animated: true, completion: nil)

Điều này hiển thị màn hình mới từ dưới lên trên nhưng tôi muốn nó hiển thị từ phải sang trái mà không cần sử dụng UINavigationController.

Tôi đang sử dụng Xib thay vì bảng phân cảnh, vậy làm cách nào để làm điều đó?


trong ngắn hạn, câu trả lời đầy đủ: stackoverflow.com/a/48081504/294884
Fattie

Câu trả lời:


158

Nó không quan trọng nếu nó là xibhoặc storyboardbạn đang sử dụng. Thông thường, chuyển đổi từ phải sang trái được sử dụng khi bạn đẩy bộ điều khiển chế độ xem vào trình chiếu UINavigiationController.

CẬP NHẬT

Đã thêm chức năng thời gian kCAMediaTimingFunctionEaseInEaseOut

Dự án mẫu có triển khai Swift 4 được thêm vào GitHub


Swift 3 & 4.2

let transition = CATransition()
transition.duration = 0.5
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
transition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)
view.window!.layer.add(transition, forKey: kCATransition)
present(dashboardWorkout, animated: false, completion: nil)


ObjC

CATransition *transition = [[CATransition alloc] init];
transition.duration = 0.5;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[self.view.window.layer addAnimation:transition forKey:kCATransition];
[self presentViewController:dashboardWorkout animated:false completion:nil];


Swift 2.x

let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
view.window!.layer.addAnimation(transition, forKey: kCATransition)
presentViewController(dashboardWorkout, animated: false, completion: nil)

Có vẻ như animatedtham số trong presentViewControllerphương thức không thực sự quan trọng trong trường hợp chuyển đổi tùy chỉnh này. Nó có thể có giá trị bất kỳ, hoặc truehoặc false.


2
Tôi đã cố gắng sử dụng mã Swift3 trong phương thức performance () của một UIStoryboardSegue tùy chỉnh, vấn đề là - quá trình chuyển đổi được thực hiện trên cùng một chế độ xem và sau đó, chế độ xem thứ hai xuất hiện. Bất kỳ ý tưởng về điều đó?
Devarshi

2
URrently khi tôi sử dụng phương pháp này, nguồn VC đang mờ dần Làm cách nào để loại bỏ hiệu ứng mờ dần đó và nó giống như hoạt ảnh đẩy?
Umair Afzal

1
@UmairAfzal Đó là màu của cửa sổ, Bạn cần thay đổi màu của cửa sổ để tránh điều đó.
Abdul Rehman

Làm thế nào để sử dụng chuyển đổi này một cách hiệu quả với UIModalPresentationStyle.overCurrentContext trong Swift 3
Mamta

3
Nếu bạn đặt "hoạt ảnh" thành "đúng", nó cũng sẽ có hoạt ảnh hướng lên một chút. Tôi khuyên bạn nên đặt nó thành "false"
Rebeloper

66

Hoàn thành mã cho hiện tại / loại bỏ, Swift 3

extension UIViewController {

    func presentDetail(_ viewControllerToPresent: UIViewController) {
        let transition = CATransition()
        transition.duration = 0.25
        transition.type = kCATransitionPush
        transition.subtype = kCATransitionFromRight
        self.view.window!.layer.add(transition, forKey: kCATransition)

        present(viewControllerToPresent, animated: false)
    }

    func dismissDetail() {
        let transition = CATransition()
        transition.duration = 0.25
        transition.type = kCATransitionPush
        transition.subtype = kCATransitionFromLeft
        self.view.window!.layer.add(transition, forKey: kCATransition)

        dismiss(animated: false)
    }
}

1
Vui lòng nói rõ hơn. Giải thích thêm !
Emm

2
Ủng hộ câu trả lời của bạn vì nó cũng có chức năng loại bỏ
Rebeloper

Một trong những tốt nhất hiện có !! 👌
Shyam

42

Đọc tất cả các câu trả lời và không thể thấy giải pháp chính xác. Cách đúng để làm như vậy là tạo UIViewControllerAnimatedTransitioning tùy chỉnh cho đại biểu VC đã trình bày.

Vì vậy, nó giả định thực hiện nhiều bước hơn, nhưng kết quả là tùy chỉnh nhiều hơn và không có một số tác dụng phụ, như chuyển từ chế độ xem cùng với chế độ xem được trình bày.

Vì vậy, giả sử bạn có một số ViewController và có một phương pháp để trình bày

var presentTransition: UIViewControllerAnimatedTransitioning?
var dismissTransition: UIViewControllerAnimatedTransitioning?    

func showSettings(animated: Bool) {
    let vc = ... create new vc to present

    presentTransition = RightToLeftTransition()
    dismissTransition = LeftToRightTransition()

    vc.modalPresentationStyle = .custom
    vc.transitioningDelegate = self

    present(vc, animated: true, completion: { [weak self] in
        self?.presentTransition = nil
    })
}

presentTransitiondismissTransitionđược sử dụng để tạo hiệu ứng cho bộ điều khiển chế độ xem của bạn. Vì vậy, bạn sử dụng ViewController của mình để UIViewControllerTransitioningDelegate:

extension ViewController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return presentTransition
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return dismissTransition
    }
}

Vì vậy, bước cuối cùng là tạo chuyển đổi tùy chỉnh của bạn:

class RightToLeftTransition: NSObject, UIViewControllerAnimatedTransitioning {
    let duration: TimeInterval = 0.25

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let toView = transitionContext.view(forKey: .to)!

        container.addSubview(toView)
        toView.frame.origin = CGPoint(x: toView.frame.width, y: 0)

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
            toView.frame.origin = CGPoint(x: 0, y: 0)
        }, completion: { _ in
            transitionContext.completeTransition(true)
        })
    }
}

class LeftToRightTransition: NSObject, UIViewControllerAnimatedTransitioning {
    let duration: TimeInterval = 0.25

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: .from)!

        container.addSubview(fromView)
        fromView.frame.origin = .zero

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseIn, animations: {
            fromView.frame.origin = CGPoint(x: fromView.frame.width, y: 0)
        }, completion: { _ in
            fromView.removeFromSuperview()
            transitionContext.completeTransition(true)
        })
    }
}

Trong bộ điều khiển chế độ xem mã đó được trình bày theo ngữ cảnh hiện tại, bạn có thể thực hiện các tùy chỉnh của mình từ thời điểm đó. Ngoài ra, bạn có thể thấy tùy chỉnh UIPresentationControllercũng hữu ích (chuyển vào sử dụng UIViewControllerTransitioningDelegate)


Làm tốt! Tôi đã cố gắng thử tất cả các câu trả lời trong một lúc và có điều gì đó không đúng ... Tôi thậm chí đã ủng hộ một câu mà lẽ ra không nên có ...
Reimond Hill

Khi nào bạn loại bỏ VC đã trình bày, bạn chỉ cần gọi bỏ qua (hoạt hình: true, hoàn thành: nil) ???
Reimond Hill

@ReimondHill vâng, tôi chỉ đang sử dụng bác bỏ thông thườngViewController
HotJard

Đây là giải pháp tốt nhất cho đến nay. Có, nó phức tạp hơn một chút, nhưng nó chắc chắn và sẽ không bị hỏng trong các điều kiện khác nhau. Câu trả lời được chấp nhận là hack.
mmh02

Nó hoạt động hoàn hảo trên tất cả các thiết bị ngoại trừ iPhone 7+, 8+, X, XMax. Bất kỳ ý tưởng tại sao? vc không lấy toàn bộ khung hình.
Umair Afzal

14

Bạn cũng có thể sử dụng segue tùy chỉnh.

Swift 5

class SegueFromRight: UIStoryboardSegue {

    override func perform() {
        let src = self.source
        let dst = self.destination

        src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
        dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width, y: 0)

        UIView.animate(withDuration: 0.25,
               delay: 0.0,
               options: UIView.AnimationOptions.curveEaseInOut,
               animations: {
                    dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
            },
                   completion: { finished in
                    src.present(dst, animated: false, completion: nil)
        })
    }
}

Câu trả lời tốt hơn sau đó khác nếu được sử dụng trong bài trình bày qua bối cảnh hiện nay
Varun Naharia

Khi loại bỏ phân biệt tùy chỉnh này, nó vẫn mặc định là loại phân xác ban đầu. Có cách nào xung quanh điều đó không?
Alex Bailey,

1
trong khi các segues tùy chỉnh là tuyệt vời - chúng chỉ là một giải pháp bảng phân cảnh
Fattie

7

Thử đi,

    let animation = CATransition()
    animation.duration = 0.5
    animation.type = kCATransitionPush
    animation.subtype = kCATransitionFromRight
     animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    vc.view.layer.addAnimation(animation, forKey: "SwitchToView")

    self.presentViewController(vc, animated: false, completion: nil)

Đây vclà bộ điều khiển chế độ xem, dashboardWorkouttrong trường hợp của bạn.


3
Cảm ơn, nhưng VC tiếp theo đột ngột xuất hiện thay vì phân cấp từ phải sang trái. Tôi đã thay đổi thời gian hoạt hình nhưng vẫn cùng
Umair Afzal

4

Nếu bạn muốn sử dụng phương pháp CATransition "sửa chữa nhanh" ....

class AA: UIViewController

 func goToBB {

    let bb = .. instantiateViewcontroller, storyboard etc .. as! AlreadyOnboardLogin

    let tr = CATransition()
    tr.duration = 0.25
    tr.type = kCATransitionMoveIn // use "MoveIn" here
    tr.subtype = kCATransitionFromRight
    view.window!.layer.add(tr, forKey: kCATransition)

    present(bb, animated: false)
    bb.delegate, etc = set any other needed values
}

và sau đó ...

func dismissingBB() {

    let tr = CATransition()
    tr.duration = 0.25
    tr.type = kCATransitionReveal // use "Reveal" here
    tr.subtype = kCATransitionFromLeft
    view.window!.layer.add(tr, forKey: kCATransition)

    dismiss(self) .. or dismiss(bb), or whatever
}

Thật không may, tất cả những điều này không thực sự chính xác :(

CATransition không thực sự được tạo ra để làm công việc này.

Lưu ý rằng bạn sẽ nhận được dấu chéo khó chịu mờ dần thành màu đen , điều này không may làm hỏng hiệu ứng.


Nhiều nhà phát triển (như tôi) thực sự không thích sử dụng NavigationController. Thông thường, sẽ linh hoạt hơn nếu chỉ trình bày theo cách đột xuất khi bạn thực hiện, đặc biệt là đối với các ứng dụng phức tạp và bất thường. Tuy nhiên, không khó để "thêm" một bộ điều khiển nav.

  1. chỉ cần trên bảng phân cảnh, đi tới mục VC và nhấp vào "nhúng -> trong bộ điều khiển điều hướng". Thực sự là vậy đó. Hoặc là,

  2. trong didFinishLaunchingWithOptionsthật dễ dàng để xây dựng bộ điều khiển nav của bạn nếu bạn thích

  3. bạn thực sự không cần phải giữ biến ở bất cứ đâu, vì .navigationControllerluôn có sẵn dưới dạng thuộc tính - thật dễ dàng.

Thực sự, khi bạn có điều hướngController, việc chuyển đổi giữa các màn hình sẽ trở nên đơn giản,

    let nextScreen = instantiateViewController etc as! NextScreen
    navigationController?
        .pushViewController(nextScreen, animated: true)

và bạn có thể pop.

Tuy nhiên, điều đó chỉ mang lại cho bạn hiệu ứng "đẩy kép" tiêu chuẩn của quả táo ...

(Cái cũ trượt ở tốc độ thấp hơn, khi cái mới trượt lên.)

Nói chung và đáng ngạc nhiên, bạn thường phải cố gắng thực hiện một quá trình chuyển đổi tùy chỉnh đầy đủ.

Ngay cả khi bạn chỉ muốn chuyển đổi đơn giản nhất, phổ biến nhất, chuyển sang / chuyển đi, bạn vẫn phải thực hiện chuyển đổi tùy chỉnh đầy đủ.

May mắn thay, để làm điều đó, có một số mã biên soạn sẵn cắt và dán trên QA này ... https://stackoverflow.com/a/48081504/294884 . Chúc mừng năm mới 2018!


3

nhập UIKit và tạo một tiện ích mở rộng cho UIViewController:

extension UIViewController {
func transitionVc(vc: UIViewController, duration: CFTimeInterval, type: CATransitionSubtype) {
    let customVcTransition = vc
    let transition = CATransition()
    transition.duration = duration
    transition.type = CATransitionType.push
    transition.subtype = type
    transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    view.window!.layer.add(transition, forKey: kCATransition)
    present(customVcTransition, animated: false, completion: nil)
}}

sau cuộc gọi simlpy:

let vC = YourViewController()
transitionVc(vc: vC, duration: 0.5, type: .fromRight)

từ trái sang phải:

let vC = YourViewController()
transitionVc(vc: vC, duration: 0.5, type: .fromleft)

bạn có thể thay đổi thời lượng với thời lượng ưa thích của mình ...


1

Thử đi.

let transition: CATransition = CATransition()
transition.duration = 0.3

transition.type = kCATransitionReveal
transition.subtype = kCATransitionFromLeft
self.view.window!.layer.addAnimation(transition, forKey: nil)
self.dismissViewControllerAnimated(false, completion: nil)

1
let transition = CATransition()
    transition.duration = 0.25
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromLeft
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    tabBarController?.view.layer.add(transition, forKey: kCATransition)
    self.navigationController?.popToRootViewController(animated: true)

Tôi đã thêm điều này trong sự kiện được nhấp vào nút của tôi cho hoạt ảnh. Nó hoạt động với tôi trong Xcode 10.2 nhanh chóng 4.
Chilli

0

trình điều khiển chế độ xem từ phải sang trái trong iOS bằng Swift

func FrkTransition() 

{
    let transition = CATransition()

    transition.duration = 2

    transition.type = kCATransitionPush

    transitioningLayer.add(transition,forKey: "transition")

    // Transition to "blue" state

    transitioningLayer.backgroundColor = UIColor.blue.cgColor
    transitioningLayer.string = "Blue"
}

Refrence By: [ https://developer.apple.com/documentation/quartzcore/catransition][1]


-1

Tôi có một giải pháp tốt hơn phù hợp với tôi nếu bạn muốn ghi lại hình ảnh động cổ điển của Apple, mà không thấy quá trình chuyển đổi "đen" mà bạn thấy bằng cách sử dụng CATransition (). Đơn giản chỉ cần sử dụng phương thức popToViewController.

Bạn có thể tìm kiếm viewController bạn cần trong

self.navigationController.viewControllers // Array of VC that contains all VC of current stack

Bạn có thể tìm viewController bạn cần bằng cách tìm kiếm restoreIdentifier của nó.

HÃY CẨN THẬN: ví dụ: nếu bạn đang điều hướng qua 3 bộ điều khiển chế độ xem và khi đến bộ điều khiển cuối cùng, bạn muốn bật bộ điều khiển đầu tiên. Bạn sẽ mất, trong trường hợp này, tham chiếu đến bộ điều khiển chế độ xem SECOND hiệu quả vì popToViewController đã ghi đè nó. BTW, có một giải pháp: bạn có thể dễ dàng lưu trước popToViewcontroller, VC mà bạn sẽ cần sau này. Nó đã làm việc cho tôi rất tốt.


-4

Điều này đã làm việc cho tôi:

self.navigationController?.pushViewController(controller, animated: true)

Tôi không muốn đẩy nó vào ngăn xếp điều hướng.
Umair Afzal
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.