【问题标题】:iPhone: How to switch tabs with an animation?iPhone:如何用动画切换标签?
【发布时间】:2011-07-06 21:53:51
【问题描述】:

我正在使用UITabBarController.selectedIndex 在标签栏驱动的应用程序中以编程方式切换标签。我要解决的问题是如何为视图之间的过渡设置动画。 IE。从当前选项卡的视图到选定选项卡的视图。

最初的想法是使用UITabBarControllerDelegate,但似乎在以编程方式切换选项卡时不会调用它。我现在正在考虑将 UITabBarDelegate.didSelectItem: 作为设置过渡动画的可能钩子。

有没有人设法为过渡设置动画? 如果是,如何?

【问题讨论】:

标签: ios4 uitabbarcontroller uitabbar


【解决方案1】:

2016 年 4 月更新: Justed 想更新此内容以感谢大家的投票。另请注意,这最初是在……ARC 之前,约束之前,……很多东西之前写的!因此,在决定是否使用这些技术时,请考虑到这一点。可能有更现代的方法。哦,如果你找到一个。请添加回复,以便所有人都能看到。谢谢。

一段时间后...

经过大量研究,我想出了两个可行的解决方案。这两个都有效,并且在选项卡之间制作了动画。

解决方案 1:从视图过渡(简单)

这是最简单的,并且使用了预定义的 UIView 转换方法。使用此解决方案,我们无需管理视图,因为该方法为我们完成了工作。

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

解决方案 2:滚动(更复杂)

一个更复杂的解决方案,但可以让您更好地控制动画。在这个例子中,我们让视图滑动打开和关闭。有了这个,我们需要自己管理视图。

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

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

【讨论】:

  • 非常感谢您的回答,效果非常好。但是我在两种解决方案中都发现了一个错误,我不确定是否每个人都会出现这种情况,但是似乎在页面转换时,导航栏和状态栏之间存在间隙,然后在动画完成后,差距关闭。这使得动画的结尾有点紧张。你知道为什么会这样吗?
  • 嗯,我的代码没有发生。这听起来很像我之前看到的一个问题,新视图框架的位置相对于窗口和状态栏不正确。尝试运行 toe 代码来交换视图而不进行转换,看看它是否仍然发生。
  • 是的,它仍然会发生而不进行转换。我尝试了第一种方法。这可能是框架的定位,我会多玩一点。我尝试将框架向上移动,并尝试将框架与 fromView 匹配,但到目前为止没有运气......
  • @EmileCormier 将其放入 TabBar 委托的 shouldSelectViewController 方法中并在那里返回 NO
  • @drekka 这对我不起作用。你能解释一下 controllerIndex 是从哪里来的吗?为什么不直接使用 tabBarControllerDelegate 方法中的 [viewController view] 作为“toView”?谢谢
【解决方案2】:

以下是我尝试使用drekka的代码形式进入delegate(UITabBarControllerDelegate)方法

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

【讨论】:

  • 你应该根据方法声明返回一些值,但是这种方法很好用+1
  • 您可以通过添加 self.delegate = self; 将委托设置为您的 UITabController 实现文件;在您的 viewDidLoad() 函数中。这将允许调用上述函数。
  • 如果您使用此代码,请记住目标视图控制器将不会收到 viewWillAppear 调用,直到 之后 它的视图出现,当 selectedIndex 更改在完成处理程序中。这可能会导致目标视图控制器出现奇怪的行为。您可以在转换之前调用viewWillAppear,但之后会被调用两次。
【解决方案3】:

我的iOS7.0以上解决方案。

您可以在标签栏的委托中指定自定义动画控制器。

像这样实现一个动画控制器:

@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

然后在你的 UITabBarControllerDelegate 中使用它:

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

【讨论】:

  • 记得把你的De​​legate连接到TabViewController的delegate outlet。工作得很漂亮。这里是最干净的解决方案。
  • 现在我正在研究 IOS 10.x 中的这个功能,这可以通过故事板和 swift 完成吗?
【解决方案4】:

使用tabBarController:shouldSelectViewController:而不是使用tabBarController: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()
    }
}

【讨论】:

  • 这似乎是最好的答案。这个解决方案没有问题。
【解决方案5】:

我认为您可以使用 CATransition 轻松实现 UITabBarControlelr 的转换;这也将解决使用 transitionFromView:toView 的任何副作用:

在从 UITabBarController 扩展的自定义 TabBarController 类中使用它。

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

希望这会有所帮助:)

【讨论】:

  • 我认为我们可以使用“shouldSelectViewController”而不是“didSelectViewController”
【解决方案6】:

在这里尝试了各种答案后,我写了post

代码在 Swift 中,您可以通过调用 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
        })
}

如果您希望所有选项卡更改都具有动画,则将其挂钩到 UITabBarControllerDelegate,如下所示:

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
}

【讨论】:

  • 这很干净,动画也很漂亮。
【解决方案7】:

我在 Swift 中的解决方案:

创建自定义 TabBar 类并将其设置在情节提要的 TabBar 中

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
 }

}

【讨论】:

  • 这在 ios9 中不起作用 - 从查找方法返回的错误,即从“[UIViewController] 向下转换?”到 '[UIViewController]' 只解开可选项;您的意思是使用“!”吗?
  • 这几乎是好的,除了我遇到了一个不会动画的错误(finished 将是错误的)。我不知道为什么会发生这种情况,但我认为这与 CA 转换有关,它认为“没有任何动画”。我改用帧动画,效果很好。
【解决方案8】:

我使用了@Mofumofu 的解决方案并将其升级到 Swift 1.2 并且还实现了向上/向下动画。 意思是,如果新视图控制器的索引大于旧视图控制器的索引,则新视图控制器会出现并将旧视图控制器向上推。否则方向向下。

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

        }
    }

}

在容器视图控制器中:

extension XYViewController: UITabBarControllerDelegate {

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

}

【讨论】:

    【解决方案9】:

    这是我的 Swift 3 解决方案:

    我像这样覆盖我的 UITabBarViewController 的 selectedIndex:

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

    然后我使用这个模仿原生推送/弹出动画的函数:

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

    希望对你有帮助:)

    【讨论】:

      【解决方案10】:

      对跳跃动画的修复...

      UIView * fromView = self.view.superview;

      【讨论】:

        【解决方案11】:

        这可以通过两种方式解决

        1 - 在您的 AppDelegate.m 文件中写入一次。请记住在 AppDelegate.h 中的冒号 (:) 后使用 包含 UITabBarControllerDelegate

        -(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 - 在每个 ViewController.m 文件中写入此内容

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

        希望对您有所帮助...!

        【讨论】:

        • 如何为导航控制器之间的过渡设置动画? tabBarControllerDelegate 仅适用于视图控制器。
        • 我都试过了,第一个显示新视图,然后动画它看起来很奇怪。第二个好像没什么影响。我进入与 tab2 关联的视图并将代码添加到 viewWillAppear 并对其进行了测试,标签之间没有可见的动画。
        • 用默认的 Xcode TabBarController 项目试过这个。 1 或 2 都没有运气。我真的希望他们能工作。 :) 我只是错过了什么吗?
        • 我也是,运气不好..有什么想法吗?
        【解决方案12】:

        您可以根据被点击的项目设置动画 - 在本例中,如果点击的索引大于前一个选定的索引,我们从左翻转,如果点击的索引小于前一个选定的索引,我们从右翻转。这是 Swift 4:实现 UITabBarControllerDelegate 方法

        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
        }
        

        【讨论】:

        • 这不起作用。我已经在视图控制器中实现了它
        • @devedv 这个解决方案有什么问题?您是否将 UITabBarControllerDelegate 设置为您的 ViewController?
        • 是的,我在 AppDelegate 类 AppDelegate 中做了以下操作:UIResponder、UIApplicationDelegate、UITabBarControllerDelegate {}。我是 swift 新手,您能详细说明您的答案中的步骤吗?
        • @devdev 如果这是您的 AppDelegate 类,请将上面的函数放在您的 AppDelegate 中并且应该可以工作
        【解决方案13】:

        drekka 的回答真的很棒。我稍微调整了滚动过渡,使动画看起来更像 Apple 的推送动画。我在第一个动画完成后添加了一个附加动画,以使滑动效果看起来正确。

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

        【讨论】:

          【解决方案14】:

          我想在按下按钮时在两个子视图控制器之间使用翻转过渡,并实现如下:

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

          我还将背景颜色设置为黑色,在我的情况下,我通过设置 navigationController.view.backgroundColor 来做到这一点,但在你的情况下,它可能是可以在应用程序委托中轻松设置的 window.backgroundColor。

          【讨论】:

            【解决方案15】:

            这是我的工作代码(用于 3 个标签,还没有尝试过更多!)用于动画标签之间的过渡。它主要基于drekka的解决方案,但已经在tabbar的delegate方法中实现了,所以如果你只是复制/粘贴它应该可以完成工作..(你永远不知道!)

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

            }

            【讨论】:

            • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
            • 感谢 Ferrybig 的提示!我确实尝试尽可能多地记录代码,以便更容易理解它,希望它有所帮助
            【解决方案16】:

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

            【讨论】:

              【解决方案17】:

              @samwize 答案翻译为 Swift 3- 对此有 2 个赞,创建从左到右的页面效果:

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

              【讨论】:

                【解决方案18】:

                @samwize's answer 已针对 Swift 5 更新:

                如果您希望所有选项卡更改都有动画,请使用 UITabBarControllerDelegate 并实现此方法:

                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
                }
                

                通过调用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
                  })
                }
                

                【讨论】:

                  【解决方案19】:

                  斯威夫特 4+

                  UITabBarControllerDelegate的方法应该是这样的,

                  func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
                  
                      animateToTab(toIndex: (tabBarController.viewControllers?.index(of: viewController))!)
                      return true
                  }
                  

                  方法是,

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-12-22
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多