iPhone: Wie wechsle ich mit einer Animation die Tabs?


106

Ich wechsle Tabs programmgesteuert in einer tabulatorgesteuerten Anwendung mit UITabBarController.selectedIndex. Das Problem, das ich zu lösen versuche, ist, wie der Übergang zwischen den Ansichten animiert werden kann. dh. von der Ansicht der aktuellen Registerkarte zur Ansicht der ausgewählten Registerkarte.

Der erste Gedanke war, das zu nutzen UITabBarControllerDelegate, aber es scheint, dass dies beim programmgesteuerten Wechseln der Registerkarten nicht aufgerufen wird. Ich betrachte jetzt das UITabBarDelegate.didSelectItem: als möglichen Haken, um eine Übergangsanimation festzulegen.

Hat es jemand geschafft, die Übergänge zu animieren? Wenn ja, wie?


1
FWIW, viele dieser hoch bewerteten Antworten stammen aus benutzerdefinierten Übergängen, die in Runos Antwort und auch in Hebertis Antwort beschrieben sind . Dies ist der richtige Weg, um diese benutzerdefinierten Animationen in Angriff zu nehmen. Siehe WWDC 2013-Video Benutzerdefinierte Übergänge mit Ansichtssteuerungen .
Rob

Antworten:


154

Update 04/2016: Justed wollte dies aktualisieren, um sich bei allen für alle Stimmen zu bedanken. Bitte beachten Sie auch, dass dies ursprünglich vor langer Zeit geschrieben wurde, als ... vor ARC, vor Einschränkungen, vor ... vielen Dingen! Bitte berücksichtigen Sie dies, wenn Sie entscheiden, ob Sie diese Techniken verwenden möchten. Es kann modernere Ansätze geben. Oh, und wenn Sie einen finden. Bitte fügen Sie eine Antwort hinzu, damit jeder sehen kann. Vielen Dank.

Etwas später ...

Nach langem Suchen habe ich zwei funktionierende Lösungen gefunden. Beide funktionierten und führten die Animation zwischen den Registerkarten durch.

Lösung 1: Übergang von der Ansicht (einfach)

Dies ist die einfachste und verwendet eine vordefinierte UIView-Übergangsmethode. Mit dieser Lösung müssen wir die Ansichten nicht verwalten, da die Methode die Arbeit für uns erledigt.

// Get views. controllerIndex is passed in as the controller we want to go to. 
UIView * fromView = tabBarController.selectedViewController.view;
UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view];

// Transition using a page curl.
[UIView transitionFromView:fromView 
                    toView:toView 
                  duration:0.5 
                   options:(controllerIndex > tabBarController.selectedIndex ? UIViewAnimationOptionTransitionCurlUp : UIViewAnimationOptionTransitionCurlDown)
                completion:^(BOOL finished) {
                    if (finished) {
                        tabBarController.selectedIndex = controllerIndex;
                    }
                }];

Lösung 2: Scrollen (komplexer)

Eine komplexere Lösung, mit der Sie die Animation besser steuern können. In diesem Beispiel werden die Ansichten ein- und ausgeschaltet. Mit diesem müssen wir die Ansichten selbst verwalten.

// Get the views.
UIView * fromView = tabBarController.selectedViewController.view;
UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view];

// Get the size of the view area.
CGRect viewSize = fromView.frame;
BOOL scrollRight = controllerIndex > tabBarController.selectedIndex;

// Add the to view to the tab bar view.
[fromView.superview addSubview:toView];

// Position it off screen.
toView.frame = CGRectMake((scrollRight ? 320 : -320), viewSize.origin.y, 320, viewSize.size.height);

[UIView animateWithDuration:0.3 
                 animations: ^{

                     // Animate the views on and off the screen. This will appear to slide.
                     fromView.frame =CGRectMake((scrollRight ? -320 : 320), viewSize.origin.y, 320, viewSize.size.height);
                     toView.frame =CGRectMake(0, viewSize.origin.y, 320, viewSize.size.height);
                 }

                 completion:^(BOOL finished) {
                     if (finished) {

                         // Remove the old view from the tabbar view.
                         [fromView removeFromSuperview];
                         tabBarController.selectedIndex = controllerIndex;                
                     }
                 }];

Diese Lösung in Swift:

extension TabViewController: UITabBarControllerDelegate {
      public func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {

           let fromView: UIView = tabBarController.selectedViewController!.view
           let toView  : UIView = viewController.view
           if fromView == toView {
                 return false
           }

           UIView.transitionFromView(fromView, toView: toView, duration: 0.3, options: UIViewAnimationOptions.TransitionCrossDissolve) { (finished:Bool) in

        }
        return true
   }
}

1
Vielen Dank für die Antwort, es funktioniert wirklich gut. Ich habe jedoch einen Fehler in beiden Lösungen gefunden. Ich bin mir nicht sicher, ob dies bei allen passiert. Es scheint jedoch, dass beim Übergang der Seite eine Lücke zwischen der Navigationsleiste und der Statusleiste besteht. Nach Abschluss der Animation wird die Lücke schließt sich. Dies macht das Ende der Animation etwas nervös. Wissen Sie, warum das passiert?
Enrico Susatyo

Hmm, passierte nicht mit meinem Code. Das klingt sehr nach einem Problem, das ich zuvor gesehen habe und bei dem die Positionierung des neuen Ansichtsrahmens in Bezug auf das Fenster und die Statusleiste nicht korrekt ist. Versuchen Sie, den Zehencode auszuführen, um Ansichten auszutauschen, ohne einen Übergang durchzuführen, und prüfen Sie, ob er noch auftritt.
Drekka

Ja, es tritt immer noch ohne Übergang auf. Ich habe die erste Methode ausprobiert. Es könnte die Positionierung des Rahmens sein, ich werde ein bisschen mehr damit herumspielen. Ich habe versucht, den Rahmen nach oben zu verschieben und habe versucht, den Rahmen mit dem fromView abzugleichen, habe aber bisher kein Glück ...
Enrico Susatyo

2
@ EmileCormier legte es in die shouldSelectViewControllerMethode des TabBar-Delegierten und gab dort NEIN zurück
Cheesus

2
@drekka das funktioniert bei mir nicht. Können Sie erklären, woher der controllerIndex stammt? und warum verwenden Sie nicht einfach [viewController view] aus der tabBarControllerDelegate-Methode für die 'toView'? Thnaks
Shannoga

25

Im Folgenden ist mein Versuch aufgeführt, das Codeformular drekka in der Methode delegate (UITabBarControllerDelegate) zu verwenden

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {

    NSArray *tabViewControllers = tabBarController.viewControllers;
    UIView * fromView = tabBarController.selectedViewController.view;
    UIView * toView = viewController.view;
    if (fromView == toView)
        return false;
    NSUInteger fromIndex = [tabViewControllers indexOfObject:tabBarController.selectedViewController];
    NSUInteger toIndex = [tabViewControllers indexOfObject:viewController];

    [UIView transitionFromView:fromView
                        toView:toView
                      duration:0.3
                       options: toIndex > fromIndex ? UIViewAnimationOptionTransitionFlipFromLeft : UIViewAnimationOptionTransitionFlipFromRight
                    completion:^(BOOL finished) {
                        if (finished) {
                            tabBarController.selectedIndex = toIndex;
                        }
                    }];
    return true;
}

2
Sie sollten einen Wert gemäß der Methodendeklaration zurückgeben, aber dieser Ansatz funktioniert gut +1
voromax

2
Sie können den Delegaten für Ihre UITabController-Implementierungsdatei festlegen, indem Sie self.delegate = self hinzufügen. in Ihrer viewDidLoad () Funktion. Dadurch kann die obige Funktion aufgerufen werden.
Chris Fremgen

21

Meine Lösung für iOS7.0 oder höher.

Sie können einen benutzerdefinierten Animationscontroller im Delegaten der Registerkartenleiste angeben.

Implementieren Sie einen Animationscontroller wie folgt:

@interface TabSwitchAnimationController : NSObject <UIViewControllerAnimatedTransitioning>

@end

@implementation TabSwitchAnimationController

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.2;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController* toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView* toView = toVC.view;
    UIView* fromView = fromVC.view;

    UIView* containerView = [transitionContext containerView];
    [containerView addSubview:toView];
    toView.frame = [transitionContext finalFrameForViewController:toVC];

    // Animate by fading
    toView.alpha = 0.0;
    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
                     animations:^{
                         toView.alpha = 1.0;
                     }
                     completion:^(BOOL finished) {
                         toView.alpha = 1.0;
                         [fromView removeFromSuperview];
                         [transitionContext completeTransition:YES];
                     }];
}

@end

Verwenden Sie es dann in Ihrem UITabBarControllerDelegate:

- (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
            animationControllerForTransitionFromViewController:(UIViewController *)fromVC
                                              toViewController:(UIViewController *)toVC
{
    return [[TabSwitchAnimationController alloc] init];
}

2
Denken Sie auch daran, Ihren Delegaten an den Delegatenausgang des TabViewController anzuschließen. Hat wunderbar funktioniert. Die sauberste Lösung hier.
Andrew Duncan

Kann dies über das Storyboard und schnell geschehen, nachdem ich mir diese Funktion in IOS 10.x angesehen habe?
Mobibob

16

Anstatt zu verwenden tabBarController:shouldSelectViewController:ist besser zu implementierentabBarController:animationControllerForTransitionFromViewController:toViewController:

TransitioningObject.swift

import UIKit

class TransitioningObject: NSObject, UIViewControllerAnimatedTransitioning {

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromView: UIView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
        let toView: UIView = transitionContext.viewForKey(UITransitionContextToViewKey)!

        transitionContext.containerView().addSubview(fromView)
        transitionContext.containerView().addSubview(toView)

        UIView.transitionFromView(fromView, toView: toView, duration: transitionDuration(transitionContext), options: UIViewAnimationOptions.TransitionCrossDissolve) { finished in
            transitionContext.completeTransition(true)
        }
    }

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

TabBarViewController.swift

import UIKit

    class TabBarViewController: UITabBarController, UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
    }

    // MARK: - Tabbar delegate

    func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return TransitioningObject()
    }
}

3
Dies scheint die beste Antwort zu sein. Keine Probleme mit dieser Lösung.
Sabiland

15

Ich denke, Sie können mit CATransition problemlos Übergänge für UITabBarControlelr erzielen. Dadurch werden auch alle Nebenwirkungen der Verwendung von TransitionFromView: toView: behoben.

Verwenden Sie dies in Ihrer benutzerdefinierten TabBarController-Klasse, die von UITabBarController erweitert wurde.

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController (UIViewController*)viewController {

    CATransition *animation = [CATransition animation];
    [animation setType:kCATransitionFade];
    [animation setDuration:0.25];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:
                              kCAMediaTimingFunctionEaseIn]];
    [self.view.window.layer addAnimation:animation forKey:@"fadeTransition"];
}

Hoffe das hilft :)


1
Ich denke, wir können "shouldSelectViewController" anstelle von "didSelectViewController" verwenden
Ryan Wu

13

Ich habe einen Beitrag geschrieben nachdem die verschiedenen Antworten hier ausprobiert hatte.

Der Code ist in Swift und Sie können die Registerkarte mit Animation programmgesteuert ändern, indem Sie aufrufen animateToTab.

func animateToTab(toIndex: Int) {
    let tabViewControllers = viewControllers!
    let fromView = selectedViewController!.view
    let toView = tabViewControllers[toIndex].view    
    let fromIndex = tabViewControllers.indexOf(selectedViewController!)

    guard fromIndex != toIndex else {return}

    // Add the toView to the tab bar view
    fromView.superview!.addSubview(toView)

    // Position toView off screen (to the left/right of fromView)
    let screenWidth = UIScreen.mainScreen().bounds.size.width;
    let scrollRight = toIndex > fromIndex;
    let offset = (scrollRight ? screenWidth : -screenWidth)
    toView.center = CGPoint(x: fromView.center.x + offset, y: toView.center.y)

    // Disable interaction during animation
    view.userInteractionEnabled = false

    UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {

            // Slide the views by -offset
            fromView.center = CGPoint(x: fromView.center.x - offset, y: fromView.center.y);
            toView.center   = CGPoint(x: toView.center.x - offset, y: toView.center.y);

        }, completion: { finished in

            // Remove the old view from the tabbar view.
            fromView.removeFromSuperview()
            self.selectedIndex = toIndex
            self.view.userInteractionEnabled = true
        })
}

Wenn Sie möchten, dass alle Registerkartenänderungen die Animation enthalten, haken Sie sie UITabBarControllerDelegateso ein:

func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
    let tabViewControllers = tabBarController.viewControllers!
    guard let toIndex = tabViewControllers.indexOf(viewController) else {
        return false
    }

    // Our method
    animateToTab(toIndex)

    return true
}

Das ist sehr sauber und animiert wunderschön.
Mohammad Zekrallah

9

Meine Lösung in Swift:

Erstellen Sie eine benutzerdefinierte TabBar-Klasse und legen Sie sie in Ihrer Storyboard-TabBar fest

class MainTabBarController: UITabBarController, UITabBarControllerDelegate {

override func viewDidLoad() {
    super.viewDidLoad()
    self.delegate = self
    // Do any additional setup after loading the view.
}

func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {


    let tabViewControllers = tabBarController.viewControllers!
    let fromView = tabBarController.selectedViewController!.view
    let toView = viewController.view

    if (fromView == toView) {
        return false
    }

    let fromIndex = tabViewControllers.indexOf(tabBarController.selectedViewController!)
    let toIndex = tabViewControllers.indexOf(viewController)

    let offScreenRight = CGAffineTransformMakeTranslation(toView.frame.width, 0)
    let offScreenLeft = CGAffineTransformMakeTranslation(-toView.frame.width, 0)

    // start the toView to the right of the screen


    if (toIndex < fromIndex) {
        toView.transform = offScreenLeft
        fromView.transform = offScreenRight
    } else {
        toView.transform = offScreenRight
        fromView.transform = offScreenLeft
    }

    fromView.tag = 124
    toView.addSubview(fromView)

    self.view.userInteractionEnabled = false
    UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {

        toView.transform = CGAffineTransformIdentity

        }, completion: { finished in

            let subViews = toView.subviews
            for subview in subViews{
                if (subview.tag == 124) {
                    subview.removeFromSuperview()
                }
            }
            tabBarController.selectedIndex = toIndex!
            self.view.userInteractionEnabled = true

    })

    return true
 }

}

Dies funktioniert nicht in ios9 - Fehler, der von der Suchmethode zurückgegeben wurde, dh Downcast von '[UIViewController]?' zu '[UIViewController]' werden nur Optionen ausgepackt; Wolltest du '!' verwenden?
lozflan

Dies war fast gut, außer dass ich auf einen Fehler gestoßen bin, der nicht animiert wird ( finishedfalsch sein wird). Ich weiß nicht, warum das passiert, aber ich denke, es hat mit der CA-Transformation zu tun, die denkt, es gibt "nichts zu animieren". Ich wechselte zur Animation mit Frames, und das funktionierte.
Samwize

3

Ich habe die Lösung von @ Mofumofu verwendet und auf Swift 1.2 aktualisiert und auch eine Auf- / Ab-Animation implementiert. Das heißt, der neue ViewController wird angezeigt und schiebt den alten nach oben, wenn der Index des neuen Viewcontrollers größer als der des alten ist. Ansonsten ist die Richtung unten.

class TabScrollPageAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

    let tabBarController: UITabBarController

    init(tabBarController: UITabBarController) {
        self.tabBarController = tabBarController
    }

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

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        if let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
            let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) {
                let fromView = fromVC.view
                let toView = toVC.view

                let containerView = transitionContext.containerView()

                var directionUpwardMultiplier: CGFloat = 1.0
                if let vcs = tabBarController.viewControllers as? [UIViewController],
                    let fIndex = find(vcs, fromVC),
                    let tIndex = find(vcs, toVC) {
                        directionUpwardMultiplier = (fIndex < tIndex) ? +1.0 : -1.0
                }

                containerView.clipsToBounds = false
                containerView.addSubview(toView)

                var fromViewEndFrame = fromView.frame
                fromViewEndFrame.origin.y -= (containerView.frame.height * directionUpwardMultiplier)

                let toViewEndFrame = transitionContext.finalFrameForViewController(toVC)
                var toViewStartFrame = toViewEndFrame
                toViewStartFrame.origin.y += (containerView.frame.height * directionUpwardMultiplier)
                toView.frame = toViewStartFrame

                toView.alpha = 0.0
                UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
                    toView.alpha = 1.0
                    toView.frame = toViewEndFrame
                    fromView.alpha = 0.0
                    fromView.frame = fromViewEndFrame
                }, completion: { (completed) -> Void in
                    toView.alpha = 1.0
                    fromView.removeFromSuperview()
                    transitionContext.completeTransition(completed)
                    containerView.clipsToBounds = true
                })

        }
    }

}

Im Container ViewController:

extension XYViewController: UITabBarControllerDelegate {

    func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return TabScrollPageAnimationController(tabBarController: tabBarController)
    }

}

3

Hier ist meine Swift 3-Lösung:

Ich überschreibe selectedIndex meines UITabBarViewControllers wie folgt:

override var selectedIndex: Int{
    get{
        return super.selectedIndex
    }
    set{
        animateToTab(toIndex: newValue)
        super.selectedIndex = newValue
    }
}

Dann benutze ich diese Funktion, die native Push / Pop-Animationen nachahmt:

func animateToTab(toIndex: Int) {
    guard let tabViewControllers = viewControllers, tabViewControllers.count > toIndex, let fromViewController = selectedViewController, let fromIndex = tabViewControllers.index(of: fromViewController), fromIndex != toIndex else {return}

    view.isUserInteractionEnabled = false

    let toViewController = tabViewControllers[toIndex]
    let push = toIndex > fromIndex
    let bounds = UIScreen.main.bounds

    let offScreenCenter = CGPoint(x: fromViewController.view.center.x + bounds.width, y: toViewController.view.center.y)
    let partiallyOffCenter = CGPoint(x: fromViewController.view.center.x - bounds.width*0.25, y: fromViewController.view.center.y)

    if push{
        fromViewController.view.superview?.addSubview(toViewController.view)
        toViewController.view.center = offScreenCenter
    }else{
        fromViewController.view.superview?.insertSubview(toViewController.view, belowSubview: fromViewController.view)
        toViewController.view.center = partiallyOffCenter
    }

    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .curveEaseIn, animations: {
        toViewController.view.center   = fromViewController.view.center
        fromViewController.view.center = push ? partiallyOffCenter : offScreenCenter
    }, completion: { finished in
        fromViewController.view.removeFromSuperview()
        self.view.isUserInteractionEnabled = true
    })
}

Ich hoffe, es hilft :)


2

ein Fix für die nervöse Animation ...

UIView * fromView = self.view.superview;


2

Dies kann auf zwei Arten gelöst werden

1 - Schreiben Sie dies einmal in Ihre AppDelegate.m-Datei. Denken Sie daran, UITabBarControllerDelegate mit <> nach Doppelpunkt (:) in Ihre AppDelegate.h aufzunehmen

-(void)tabBarController:(UITabBarController *)tabBarControllerThis didSelectViewController:(UIViewController *)viewController
{
    [UIView transitionWithView:viewController.view
                      duration:0.1
                       options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve
                    animations:^(void){
                    } completion:^(BOOL finished){
                        [UIView beginAnimations:@"animation" context:nil];
                        [UIView setAnimationDuration:0.7];
                        [UIView setAnimationBeginsFromCurrentState:YES];
                        [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
                                               forView:viewController.view
                                                 cache:NO];
                        [UIView commitAnimations];
                    }];
}

2 - Schreiben Sie dies in jede Ihrer ViewController.m-Dateien

-(void)viewWillAppear:(BOOL)animated
{
    [UIView transitionWithView:self.view
                      duration:1.0
                       options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve
                    animations:^(void){
                        [super viewWillAppear:YES];
                    } completion:^(BOOL finished){
                    }];
}

Ich hoffe das hilft...!


1
Wie kann ich Übergänge zwischen Navigationssteuerungen animieren? Das tabBarControllerDelegate funktioniert nur mit View Controllern.
Saeppi

Ich habe beide ausprobiert, die erste zeigte die neue Ansicht an und animierte sie dann, was seltsam aussah. Der zweite schien keinen Einfluss zu haben. Ich ging in die mit tab2 verknüpfte Ansicht und fügte den Code der viewWillAppear hinzu und testete ihn. Zwischen den Registerkarten befand sich keine sichtbare Animation.
Shannon Cole

Versuchte dies mit Standard Xcode TabBarController Projekt. Kein Glück mit 1 oder 2. Ich wollte wirklich, dass sie funktionieren. :) Vermisse ich nur etwas?
Andrew Duncan

Ich auch, kein Glück .. irgendwelche Ideen?
Jon

2

Sie können abhängig vom angezapften Element animieren. In diesem Beispiel wird FlipFromLeft verwendet, wenn der angezapfte Index> als der zuvor ausgewählte Index ist, und FlipFromRight, wenn der angezapfte Index <als der zuvor ausgewählte Index ist. Dies ist Swift 4: Implementieren Sie die UITabBarControllerDelegate-Methode

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

    let fromView: UIView = tabBarController.selectedViewController!.view
    let toView: UIView = viewController.view

    if fromView == toView {
        return false
    }

    if let tappedIndex = tabBarController.viewControllers?.index(of: viewController) {
        if tappedIndex > tabBarController.selectedIndex {
            UIView.transition(from: fromView, to: toView, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, completion: nil)
        } else {
            UIView.transition(from: fromView, to: toView, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromRight, completion: nil)
        }
    }
    return true
}

das funktioniert nicht. Ich habe es in View Controller implementiert
devedv

@devedv Was funktioniert nicht mit dieser Lösung? Haben Sie das UITabBarControllerDelegate auf Ihren ViewController gesetzt?
Teetz

Ja, ich habe in der AppDelegate-Klasse AppDelegate Folgendes getan: UIResponder, UIApplicationDelegate, UITabBarControllerDelegate {}. Ich bin neu bei Swift. Können Sie die Schritte in Ihrer Antwort erläutern?
Devedv

@devdev Wenn dies Ihre AppDelegate-Klasse ist, fügen Sie die obige Funktion in Ihr AppDelegate ein und sollte funktionieren
Teetz


1

Drekkas Antwort ist wirklich großartig. Ich habe den Bildlaufübergang ein wenig angepasst, damit die Animation eher wie die Push-Animation von Apple aussieht. Nach Abschluss der ersten Animation habe ich eine zusätzliche Animation hinzugefügt, damit dieser Gleiteffekt richtig aussieht.

// Disable interaction during animation to avoids bugs.
self.tabBarController.view.userInteractionEnabled = NO;

// Get the views.
UIView * fromView = tabBarController.selectedViewController.view;
UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view];

// Get the size of the view area.
CGRect viewSize = fromView.frame;
BOOL scrollRight = controllerIndex > tabBarController.selectedIndex;

// Add the to view to the tab bar view.
[fromView.superview addSubview:toView];
[fromView.superview addSubview:fromView];

self.tabBarController.selectedIndex = 0;

// Position it off screen.
toView.frame = CGRectMake((scrollRight ? (viewSize.size.width *.25) : -(viewSize.size.width * .25 )), viewSize.origin.y, viewSize.size.width, viewSize.size.height);

[UIView animateWithDuration:0.25 
             animations: ^{
                 // Animate the views on and off the screen.
                 [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                 fromView.frame = CGRectMake(viewSize.size.width * .95, viewSize.origin.y, viewSize.size.width, viewSize.size.height);
                 toView.frame = CGRectMake((viewSize.origin.x * .90), viewSize.origin.y, viewSize.size.width, viewSize.size.height);
             }

             completion:^(BOOL finished) {
                 if (finished) {
                     // Being new animation.
                     [UIView animateWithDuration:0.2
                                          animations: ^{
                                              [UIView setAnimationCurve:UIViewAnimationCurveLinear];
                                              fromView.frame = CGRectMake(viewSize.size.width, viewSize.origin.y, viewSize.size.width, viewSize.size.height);
                                              toView.frame = CGRectMake((viewSize.origin.x), viewSize.origin.y, viewSize.size.width, viewSize.size.height);
                                          }
                                          completion:^(BOOL finished) {
                                              if (finished) {
                                                  // Remove the old view from the tabbar view.
                                                  [fromView removeFromSuperview];
                                                  // Restore interaction.
                                                  self.tabBarController.view.userInteractionEnabled = YES;
                                              }
                                          }];
                 }
             }];

0

Ich wollte einen Flip-Übergang zwischen zwei untergeordneten Ansichts-Controllern auf Knopfdruck verwenden und habe ihn wie folgt erreicht:

-(IBAction)flipViewControllers:(id)sender{
    NSUInteger index = self.selectedIndex;
    index++;
    if(index >= self.childViewControllers.count){
        index = 0;
    }

    self.selectedIndex = index;

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.75];
    [UIView setAnimationTransition:index % 2 ? UIViewAnimationTransitionFlipFromLeft : UIViewAnimationTransitionFlipFromRight
                           forView:self.view
                             cache:YES];
    [UIView commitAnimations];
}

Ich habe auch die Hintergrundfarbe auf Schwarz gesetzt. In meinem Fall habe ich dazu die Datei navigationController.view.backgroundColor festgelegt. In Ihrem Fall kann es sich jedoch um die window.backgroundColor handeln, die einfach im App-Delegaten festgelegt werden kann.


0

Hier ist mein Arbeitscode ( für 3 Registerkarten , habe ihn nicht mehr ausprobiert !!), um Übergänge zwischen Registerkarten zu animieren. Es basiert hauptsächlich auf der Lösung von drekka, ist jedoch bereits in der Delegatenmethode der Registerkarte implementiert. Daher sollte es den Job erledigen, wenn Sie es nur kopieren / einfügen. (Sie wissen es nie!)

-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {

// Important! We validate that the selected tab is not the current tab, to avoid misplacing views
if (tabBarController.selectedViewController == viewController) {
    return NO;
}

// Find the selected view's index
NSUInteger controllerIndex = 0;
for (UIViewController *vc in tabBarController.viewControllers) {
    if (vc == viewController) {
        controllerIndex = [tabBarController.viewControllers indexOfObject:vc];
    }
}

CGFloat screenWidth = SCREEN_SIZE.width;

// Note: We must invert the views according to the direction of the scrolling ( FROM Left TO right or FROM right TO left )
UIView * fromView = tabBarController.selectedViewController.view;
UIView * toView = viewController.view;

[fromView.superview addSubview:toView];
CGRect fromViewInitialFrame = fromView.frame;
CGRect fromViewNewframe = fromView.frame;

CGRect toViewInitialFrame = toView.frame;

if ( controllerIndex > tabBarController.selectedIndex ) {
// FROM left TO right ( tab0 to tab1 or tab2 )

    // The final frame for the current view. It will be displaced to the left
    fromViewNewframe.origin.x = -screenWidth;
    // The initial frame for the new view. It will be displaced to the left
    toViewInitialFrame.origin.x = screenWidth;
    toView.frame = toViewInitialFrame;

} else {
// FROM right TO left ( tab2 to tab1 or tab0 )

    // The final frame for the current view. It will be displaced to the right
    fromViewNewframe.origin.x = screenWidth;
    // The initial frame for the new view. It will be displaced to the right
    toViewInitialFrame.origin.x = -screenWidth;
    toView.frame = toViewInitialFrame;
}

[UIView animateWithDuration:0.2 animations:^{
    // The new view will be placed where the initial view was placed
    toView.frame = fromViewInitialFrame;
    // The initial view will be place outside the screen bounds
    fromView.frame = fromViewNewframe;

    tabBarController.selectedIndex = controllerIndex;

    // To prevent user interaction during the animation
    [[UIApplication sharedApplication] beginIgnoringInteractionEvents];

} completion:^(BOOL finished) {

    // Before removing the initial view, we adjust its frame to avoid visual lags
    fromView.frame = CGRectMake(0, 0, fromView.frame.size.width, fromView.frame.size.height);
    [fromView removeFromSuperview];

    [[UIApplication sharedApplication] endIgnoringInteractionEvents];
}];

return NO;

}}


Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Ferrybig

Danke für den Tipp Ferrybig! Ich habe versucht, den Code so weit wie möglich zu dokumentieren, um das Verständnis zu erleichtern. Ich hoffe, es hilft
Nahuel Roldan

0

Das funktioniert bei mir in Swift 3:

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

    if let fromView = tabBarController.selectedViewController?.view, let toView = viewController.view {

        if fromView == toView {
            return false
        }

        UIView.transition(from: fromView, to: toView, duration: 0.2, options: .transitionCrossDissolve) { (finished) in
        }
    }

    return true
}

0

@samwize Antwort übersetzt in Swift 3 - 2 Daumen hoch auf diesem, erzeugt einen von links nach rechts gerichteten Seiteneffekt:

func animateToTab(toIndex: Int) {
        let tabViewControllers = viewControllers!
        let fromView = selectedViewController!.view
        let toView = tabViewControllers[toIndex].view
        let fromIndex = tabViewControllers.index(of: selectedViewController!)

        guard fromIndex != toIndex else {return}

        // Add the toView to the tab bar view
        fromView?.superview!.addSubview(toView!)

        // Position toView off screen (to the left/right of fromView)
        let screenWidth = screenSize.width
        let scrollRight = toIndex > fromIndex!
        let offset = (scrollRight ? screenWidth : -screenWidth)
        toView?.center = CGPoint(x: (fromView?.center.x)! + offset, y: (toView?.center.y)!)

        // Disable interaction during animation
        view.isUserInteractionEnabled = false

        UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {

            // Slide the views by -offset
            fromView?.center = CGPoint(x: (fromView?.center.x)! - offset, y: (fromView?.center.y)!);
            toView?.center   = CGPoint(x: (toView?.center.x)! - offset, y: (toView?.center.y)!);

        }, completion: { finished in

            // Remove the old view from the tabbar view.
            fromView?.removeFromSuperview()
            self.selectedIndex = toIndex
            self.view.isUserInteractionEnabled = true
        })
    }

0

@ samwizes Antwort für Swift 5 aktualisiert:

Wenn alle Registerkartenänderungen animiert werden sollen, verwenden Sie ein UITabBarControllerDelegate und implementieren Sie diese Methode:

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
  let tabViewControllers = tabBarController.viewControllers!
  guard let toIndex = tabViewControllers.indexOf(value:viewController) else {
    return false
  }
  animateToTab(toIndex: toIndex, fadeOutFromView: false, fadeInToView: false)
  return true
}

Ändern Sie die Registerkarte mit der Animation programmgesteuert, indem Sie Folgendes aufrufen animateToTab:

func animateToTab(toIndex: Int, fadeOutFromView: Bool, fadeInToView: Bool) {
  let tabViewControllers = viewControllers!
  let fromView = selectedViewController!.view
  let toView = tabViewControllers[toIndex].view
  let fromIndex = tabViewControllers.indexOf(value:selectedViewController!)
  guard fromIndex != toIndex else {return}

  // Add the toView to the tab bar view
  fromView!.superview!.addSubview(toView!)

  // Position toView off screen (to the left/right of fromView)
  let screenWidth = UIScreen.main.bounds.width
  let scrollRight = toIndex > fromIndex!;
  let offset = (scrollRight ? screenWidth : -screenWidth)
  toView!.center = CGPoint(x: fromView!.center.x + offset, y: toView!.center.y)

  // Disable interaction during animation
  view.isUserInteractionEnabled = false
  if fadeInToView {
    toView!.alpha = 0.1
  }

  UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: [.curveEaseOut], animations: {

    if fadeOutFromView {
      fromView!.alpha = 0.0
    }

    if fadeInToView {
      toView!.alpha = 1.0
    }

    // Slide the views by -offset
    fromView!.center = CGPoint(x: fromView!.center.x - offset, y: fromView!.center.y);
    toView!.center   = CGPoint(x: toView!.center.x - offset, y: toView!.center.y);

  }, completion: { finished in
    // Remove the old view from the tabbar view.
    fromView!.removeFromSuperview()
    self.selectedIndex = toIndex
    self.view.isUserInteractionEnabled = true
  })
}

-2

Swift 4+

Ihre UITabBarControllerDelegateMethode sollte so sein,

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

    animateToTab(toIndex: (tabBarController.viewControllers?.index(of: viewController))!)
    return true
}

Und die Methode ist,

func animateToTab(toIndex: Int) {
    let tabViewControllers = viewControllers!
    let fromView = selectedViewController!.view
    let toView = tabViewControllers[toIndex].view
    let fromIndex = tabViewControllers.index(of: selectedViewController!)

    guard fromIndex != toIndex else {return}

    // Add the toView to the tab bar view
    fromView!.superview!.addSubview(toView!)

    // Position toView off screen (to the left/right of fromView)
    let screenWidth = UIScreen.main.bounds.size.width;
    let scrollRight = toIndex > fromIndex!;
    let offset = (scrollRight ? screenWidth : -screenWidth)
    toView!.center = CGPoint(x: fromView!.center.x + offset, y: toView!.center.y)

    // Disable interaction during animation
    view.isUserInteractionEnabled = false

    UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {

        // Slide the views by -offset
        fromView!.center = CGPoint(x: fromView!.center.x - offset, y: fromView!.center.y);
        toView!.center   = CGPoint(x: toView!.center.x - offset, y: toView!.center.y);

    }, completion: { finished in

        // Remove the old view from the tabbar view.
        fromView!.removeFromSuperview()
        self.selectedIndex = toIndex
        self.view.isUserInteractionEnabled = true
    });

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.