视图控制器滑动动画

12

我想要创建一种动画,就像iOS应用程序facebook在标签切换时所展示的那样[1]。我已经尝试开发某种类型的动画,但问题是旧的视图控制器在切换时直接变为不可见,而不是慢慢褪色,同时新的控制器快速滑入。

我找到了这个SO问题如何使用CrossDissolve幻灯片转换来动画化选项卡栏的选项卡切换?,但被标记为正确答案的解决方案对我实际上并不起作用(它不是幻灯片转换,而是淡入淡出转换)。我还想获得的功能是使滑动左侧或右侧可以切换选项卡,就像旧版本的Facebook一样。

到目前为止,我拥有的是:

extension TabBarController: UITabBarControllerDelegate  {
    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        guard let fromView = selectedViewController?.view,
              let toView = viewController.view else { return false }
        if fromView != toView {
            toView.transform = CGAffineTransform(translationX: -90, y: 0)
            UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
                toView.transform = CGAffineTransform(translationX: 0, y: 0)
            })
        }; return true
    }
}

class TabBarController: UITabBarController {
    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
    }
}

如何修复这个问题?


[1] 我非常想在Facebook应用程序中添加一个gif。问题是,我不想对视频进行审查,只是揭示我的数据太多了。(即使fb已经有它们)。在YouTube上我也没有找到合适的录制。请自行在iOS上的fb应用程序中尝试。


那么,为什么你提到的答案不适合你呢? - Taras Chernyshenko
这里有一个使用 UIViewControllerAnimatedTransitioning 的要点:https://gist.github.com/dsoike/caa34a2605306f28c3061efc4920ba13 - Derek Soike
3个回答

8
你可以使用以下思路:https://samwize.com/2016/04/27/making-tab-bar-slide-when-selected/ 此外,这是更新为Swift 4.1的代码,我还删除了强制解包。
import UIKit

class MyTabBarController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        delegate = self
    }
}

extension MyTabBarController: UITabBarControllerDelegate  {
    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        guard let tabViewControllers = tabBarController.viewControllers, let toIndex = tabViewControllers.index(of: viewController) else {
            return false
        }
        animateToTab(toIndex: toIndex)
        return true
    }

    func animateToTab(toIndex: Int) {
        guard let tabViewControllers = viewControllers,
            let selectedVC = selectedViewController else { return }

        guard let fromView = selectedVC.view,
            let toView = tabViewControllers[toIndex].view,
            let fromIndex = tabViewControllers.index(of: selectedVC),
            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.3,
                       delay: 0.0,
                       usingSpringWithDamping: 1,
                       initialSpringVelocity: 0,
                       options: .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
        })
    }
}

因此,您需要子类化UITabBarController并编写动画部分,您可以调整动画选项(延迟,持续时间等)。

希望对您有所帮助,干杯!

输入图像描述


当然,给你。 - Mihai Erős
1
哦,是的,我正在阅读其他东西。从我的角度来看,不知道那是否是一个好的UI/UX,你开始谈论Facebook应用程序具有的某个功能,但滑动手势在Facebook应用程序中不存在。你应该发布一个新问题,你问了关于动画的问题... - Mihai Erős
当转换发生时,控制器的顶部栏颜色会改变,从而导致令人讨厌的闪烁效果。如何解决?请查看视频 https://drive.google.com/file/d/15mlR9NfV_1C8gwi-4vfpV4CxpXbG5BUm/view - Utku Dalmaz
@UtkuDalmaz,你能在Github或其他地方发布一份代码样例吗?如果我能重现你的问题,我可以帮你看看。 - Mihai Erős
我已经发布了我的代码。我一开始使用了你的代码,但后来改成了类似的代码,这里是 https://stackoverflow.com/questions/62343102/tabbar-transition-animation-topbar-color-issue 你能帮我解决这个问题吗? - Utku Dalmaz

8
我从未见过Facebook,所以不知道动画是什么。但是,当选项卡栏控制器更改其选项卡(子视图控制器)时,您可以使用苹果提供的内置机制添加自定义动画来使其协调和没有任何黑客攻击。这称为自定义转场动画
苹果于2013年首次引入了此机制。以下是他们关于此的视频链接:https://developer.apple.com/videos/play/wwdc2013/218/ 我立即在我的应用程序中采用了这种方法,并认为它使它们看起来更加漂亮。以下是我喜欢的选项卡栏控制器自定义转场的演示:

enter image description here

真正酷的是,一旦你决定了想要的动画,让转换变得交互式(即使用手势驱动它而不是按钮点击)很容易:

enter image description here

现在,你可能会说:好吧,但这并不是我想要的动画。没问题!一旦掌握了自定义转场架构,将动画改为任何你喜欢的东西都很容易。在这个变体中,我只是注释掉了一行代码,以便“旧”视图控制器不会滑动:

enter image description here

所以让你的想象力奔放吧!采用自定义过渡动画,就像iOS所期望的那样。

1
你能同时发布一些代码吗?我真的很想看看你是如何实现这个自定义转换效果的。 - Mihai Erős
2
有关生成这些转换的代码,请参见https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch06p300customAnimation3b/ch19p620customAnimation1/AppDelegate.swift。 - matt
感谢@matt提供的答案和WWDC视频链接。 - Nikhil Muskur

2

如果你想要实现pushViewController导航的功能,可以尝试这个方法。

然而,在TabBarController上切换选项卡时,这个方法将不起作用。为此,我会选择@mihai-erős的解决方案。

根据你的喜好更改动画持续时间,并将这个类分配给你的导航segue,以进行幻灯片动画。

class CustomPushSegue: UIStoryboardSegue {

    override func perform() {
        // first get the source and destination view controllers as UIviews so that they can placed in navigation stack

        let sourceVCView = self.source.view as UIView!
        let destinationVCView = self.destination.view as UIView!
        let screenWidth = UIScreen.main.bounds.size.width

        //create the destination view's rectangular frame i.e starting at 0,0 and equal to screenwidth by screenheight
        destinationVCView?.transform = CGAffineTransform(translationX: screenWidth, y: 0)

        //the destinationview needs to be placed on top(aboveSubView) of source view in the app window stack before being accessed by nav stack
        // get the window and insert destination View
        let window = UIApplication.shared.keyWindow
        window?.insertSubview(destinationVCView!, aboveSubview: sourceVCView!)


        // the animation: first remove the source out of screen by moving it at the left side of it and at the same time place the destination to source's position
        // Animate the transition.
        UIView.animate(withDuration: 0.3, animations: { () -> Void in
            sourceVCView?.transform = CGAffineTransform(translationX: -screenWidth,y: 0)
            destinationVCView?.transform = CGAffineTransform.identity

        }, completion: { (Finished) -> Void in
            self.source.present(self.destination, animated: false, completion: nil)
        }) 
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接