Swift 中的 TabBar 图标弹跳效果就像 Twitter 应用程序一样
TabBar icon bounce effect on selection like a Twitter app in Swift
当我点击其中一个应用程序时,如何创建像 Twitter 应用程序那样的标签栏图标弹跳效果?看起来它缩小了,然后又恢复正常了。
谢谢。
嘿,这不是完美的代码,但应该让您了解如何实现自定义 TabBarViewController。我没有写视图布局部分,所以你应该处理它,而且标签项动画不在这里。
class TabBarViewController: UIViewController, TabBarViewDelegate {
private var containerView: UIView!
private var tabBar: TabBarView!
var selectedTabIndex: Int! {
didSet {
if oldValue != nil && oldValue != selectedTabIndex {
let previousVc = childViewControllers[oldValue]
previousVc.beginAppearanceTransition(false, animated: false)
previousVc.view.removeFromSuperview()
previousVc.endAppearanceTransition()
}
let vc = childViewControllers[selectedTabIndex]
vc.beginAppearanceTransition(true, animated: false)
containerView.addSubview(vc.view)
vc.endAppearanceTransition()
}
}
override func loadView() {
let viewControllers = [YourViewController1(), YourViewController2()]
containerView = UIView()
tabBar = TabBarView(numberOfTabs: viewControllers.count)
tabBar.delegate = self
viewcontrollers.forEach {
addChildViewController([=10=])
[=10=].didMove(toParentViewController: self)
}
view = UIView()
view.addSubview(containerView)
view.addSubview(tabBar)
selectedTabIndex = 0
}
func didSelect(tab: Int) {
if selectedTabIndex != tab {
selectedTabIndex = tab
}
}
}
protocol TabBarViewDelegate: class {
func didSelect(tab: Int)
}
class TabBarView: UIView {
private let tabs: [TabBarItemView]
weak var delegate: TabBarViewDelegate? {
didSet {
tabs.forEach {
[=10=].delegate = delegate
}
}
}
init(numberOfTabs: Int) {
let tabs = (0...numberOfTabs).map({TabBarItemView(index: [=10=])})
super.init(frame: .zero)
let tabsStackView = UIStackView(arrangedSubviews: tabs) //.horizontal axis
addSubview(tabsStackView)
}
}
class TabBarItemView: UIView {
weak var delegate: TabBarViewDelegate?
private let index: Int
private let imageView = UIImageView(image: UIImage(named: ""))
init(index: Int) {
self.index = index
super.init(frame: .zero)
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(onSelected))
gestureRecognizers = [tap]
addSubview(imageView)
}
@objc private func onSelected() {
//animate imageView
delegate?.didSelect(tab: index)
}
}
我知道怎么做。轻松玩!!!
假设有 5 个标签栏项目,标签是 0,1,2,3,4,那么在你的标签栏控制器中:
class xxxVC: UITabBarController {
var storedImageViewArr:[UIImageView?] = []
private var times = [Int].init(repeating: 0, count: 5)
private var tempTimes = [Int].init(repeating: 0, count: 5)
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
override func viewDidLoad() {
super.viewDidLoad()
// create image views
getImageView()
//set path
setPath()
}//xxxVC class over line
//custom functions
extension xxxVC{
fileprivate func getImageView(){
times[0] = 1
storedImageViewArr = [0,1,2,3,4].map{ (offset) -> UIImageView in
let tempImageView = self.tabBar.subviews[offset].subviews.first as! UIImageView
tempImageView.contentMode = .center
return tempImageView
}
fileprivate func setPath(){
bounceAnimation.values = [1.0 ,0.6, 0.9, 1.15, 0.95, 1.02, 1.0]
bounceAnimation.duration = TimeInterval(0.5)
bounceAnimation.calculationMode = kCAAnimationCubic
}
}
//UITabBarControllerDelegate
extension xxxVC{
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
//the arr in this count is sure, so it costs O(1)
if times[item.tag] == 0{
setAnimation(at: item.tag)
times = tempTimes
times[item.tag] += 1;return
}
}
}
所以,你可以做到。
这是一个非常简单的解决方案,适合我。子类化 UITabBarController
并覆盖 func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem)
class AnimatedTabBarController: UITabBarController {
private var bounceAnimation: CAKeyframeAnimation = {
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
bounceAnimation.values = [1.0, 1.4, 0.9, 1.02, 1.0]
bounceAnimation.duration = TimeInterval(0.3)
bounceAnimation.calculationMode = CAAnimationCalculationMode.cubic
return bounceAnimation
}()
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// find index if the selected tab bar item, then find the corresponding view and get its image, the view position is offset by 1 because the first item is the background (at least in this case)
guard let idx = tabBar.items?.index(of: item), tabBar.subviews.count > idx + 1, let imageView = tabBar.subviews[idx + 1].subviews.first as? UIImageView else {
return
}
imageView.layer.add(bounceAnimation, forKey: nil)
}
}
结果是这样的
当我点击其中一个应用程序时,如何创建像 Twitter 应用程序那样的标签栏图标弹跳效果?看起来它缩小了,然后又恢复正常了。
谢谢。
嘿,这不是完美的代码,但应该让您了解如何实现自定义 TabBarViewController。我没有写视图布局部分,所以你应该处理它,而且标签项动画不在这里。
class TabBarViewController: UIViewController, TabBarViewDelegate {
private var containerView: UIView!
private var tabBar: TabBarView!
var selectedTabIndex: Int! {
didSet {
if oldValue != nil && oldValue != selectedTabIndex {
let previousVc = childViewControllers[oldValue]
previousVc.beginAppearanceTransition(false, animated: false)
previousVc.view.removeFromSuperview()
previousVc.endAppearanceTransition()
}
let vc = childViewControllers[selectedTabIndex]
vc.beginAppearanceTransition(true, animated: false)
containerView.addSubview(vc.view)
vc.endAppearanceTransition()
}
}
override func loadView() {
let viewControllers = [YourViewController1(), YourViewController2()]
containerView = UIView()
tabBar = TabBarView(numberOfTabs: viewControllers.count)
tabBar.delegate = self
viewcontrollers.forEach {
addChildViewController([=10=])
[=10=].didMove(toParentViewController: self)
}
view = UIView()
view.addSubview(containerView)
view.addSubview(tabBar)
selectedTabIndex = 0
}
func didSelect(tab: Int) {
if selectedTabIndex != tab {
selectedTabIndex = tab
}
}
}
protocol TabBarViewDelegate: class {
func didSelect(tab: Int)
}
class TabBarView: UIView {
private let tabs: [TabBarItemView]
weak var delegate: TabBarViewDelegate? {
didSet {
tabs.forEach {
[=10=].delegate = delegate
}
}
}
init(numberOfTabs: Int) {
let tabs = (0...numberOfTabs).map({TabBarItemView(index: [=10=])})
super.init(frame: .zero)
let tabsStackView = UIStackView(arrangedSubviews: tabs) //.horizontal axis
addSubview(tabsStackView)
}
}
class TabBarItemView: UIView {
weak var delegate: TabBarViewDelegate?
private let index: Int
private let imageView = UIImageView(image: UIImage(named: ""))
init(index: Int) {
self.index = index
super.init(frame: .zero)
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(onSelected))
gestureRecognizers = [tap]
addSubview(imageView)
}
@objc private func onSelected() {
//animate imageView
delegate?.didSelect(tab: index)
}
}
我知道怎么做。轻松玩!!!
假设有 5 个标签栏项目,标签是 0,1,2,3,4,那么在你的标签栏控制器中:
class xxxVC: UITabBarController {
var storedImageViewArr:[UIImageView?] = []
private var times = [Int].init(repeating: 0, count: 5)
private var tempTimes = [Int].init(repeating: 0, count: 5)
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
override func viewDidLoad() {
super.viewDidLoad()
// create image views
getImageView()
//set path
setPath()
}//xxxVC class over line
//custom functions
extension xxxVC{
fileprivate func getImageView(){
times[0] = 1
storedImageViewArr = [0,1,2,3,4].map{ (offset) -> UIImageView in
let tempImageView = self.tabBar.subviews[offset].subviews.first as! UIImageView
tempImageView.contentMode = .center
return tempImageView
}
fileprivate func setPath(){
bounceAnimation.values = [1.0 ,0.6, 0.9, 1.15, 0.95, 1.02, 1.0]
bounceAnimation.duration = TimeInterval(0.5)
bounceAnimation.calculationMode = kCAAnimationCubic
}
}
//UITabBarControllerDelegate
extension xxxVC{
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
//the arr in this count is sure, so it costs O(1)
if times[item.tag] == 0{
setAnimation(at: item.tag)
times = tempTimes
times[item.tag] += 1;return
}
}
}
所以,你可以做到。
这是一个非常简单的解决方案,适合我。子类化 UITabBarController
并覆盖 func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem)
class AnimatedTabBarController: UITabBarController {
private var bounceAnimation: CAKeyframeAnimation = {
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
bounceAnimation.values = [1.0, 1.4, 0.9, 1.02, 1.0]
bounceAnimation.duration = TimeInterval(0.3)
bounceAnimation.calculationMode = CAAnimationCalculationMode.cubic
return bounceAnimation
}()
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// find index if the selected tab bar item, then find the corresponding view and get its image, the view position is offset by 1 because the first item is the background (at least in this case)
guard let idx = tabBar.items?.index(of: item), tabBar.subviews.count > idx + 1, let imageView = tabBar.subviews[idx + 1].subviews.first as? UIImageView else {
return
}
imageView.layer.add(bounceAnimation, forKey: nil)
}
}
结果是这样的