额...貌似挺长时间没有总结新知识了,最近在看swift,之前swift刚出来的时候大体看了一遍,后来时间长了没看加之swift2.0做了比较大的调整,公司项目也不是用swift写的,也就没怎么看了,谁成想忘的差不多了,趁公司最近项目不忙,有抽时间看了一丢丢,感觉这知识真是看一遍有一遍的收获,最近看了一个效果感觉挺好玩的.就是带有动画效果的TabBarItem,在这里总结一下.-----以上是为了给自己一个警醒,要多总结知识,加油,小青年,未来是你的!!!

首先,效果图如下

PageBlurTestGif.gif

这是一个大牛高仿的爱鲜蜂的效果,本着学习的态度跟着实现了一下这个功能,记录一下心得.功能主要是分两块,一个就是首次进入app时会有一个引导页,是用UICollectionView实现的.第二个功能就是带有动画效果的TabBarItem

首先是引导页功能的实现:
在程序打开时在AppDelegate

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {...}

在设置rootViewController时使用NSUserDefaults.standardUserDefaults()在应用打开运行时是否能根据字典找到value,如果value为nil说明是第一次进入app,然后进入引导页,并且给NSUserDefaults.standardUserDefaults()赋值,这样下次再打开应用的时候就能根据key找到value值了,通过这个道理来判断是否是第一次进入app,代码:

window = UIWindow(frame: ScreenBounds)
window?.makeKeyAndVisible() //判断是否是第一次开启应用
let isFirstOpen = NSUserDefaults.standardUserDefaults().objectForKey("First")
if isFirstOpen == nil {
//第一次打开应用
window?.rootViewController = GuideViewController() NSUserDefaults.standardUserDefaults().setObject("First", forKey: "First")
} else {
//不是第一次打开应用
loadHomeViewController()
}

第一次打开应用:
利用UICollectionView创建,1:先看cell中的内容,将imageView和Button放在collectionViewCell中,在这里需要注意,在前三页中没有"立即体验"button,
所以在自定义cell的时候需要一个函数在外面调用以改变button的隐藏与否:

func setNextBtnHidden(hidden:Bool) {
nextBtn.hidden = hidden
}

当滑动到第四页的时候,"立即体验"按钮出现点击按钮触发通知,通知controller进行界面跳转

func nextBtnClick() {
NSNotificationCenter.defaultCenter().postNotificationName(GuideViewControllerDidFinish, object: nil)
}

2:再看collectionViewController中的相关代码,collectionView需要layout:

let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.itemSize = ScreenBounds.size
layout.scrollDirection = .Horizontal

然后设置collectionView:

 collectionView = UICollectionView(frame: ScreenBounds, collectionViewLayout: layout)
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.showsVerticalScrollIndicator = false
collectionView?.pagingEnabled = true
collectionView?.bounces = false
collectionView?.registerClass(GuideCollectionViewCell.self , forCellWithReuseIdentifier: cellIdentifier)
view.addSubview(collectionView!)

注意:这一步在初始化collectionView的时候不要忘记registerCell
设置pageControl:

private var pageController = UIPageControl(frame: CGRectMake(0, ScreenHeight - 50, ScreenWidth, 20))
private func createPageControll() {
pageController.numberOfPages = imageNames.count
pageController.currentPage = 0
view.addSubview(pageController)
}

然后实现collectionView的delegate和dataSource

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageNames.count
}
返回一共有多少item
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as! GuideCollectionViewCell
cell.newImage = UIImage(named: imageNames[indexPath.row])
if indexPath.row != imageNames.count - 1 {
cell.setNextBtnHidden(true)
}
return cell
}
设置自定义的cell ,并且在导航页不是最后一页的时候调用cell中设置Button Hidden的函数将Button隐藏

scrollView的代理方法:

func scrollViewDidEndDecelerating(scrollView: UIScrollView) {

        if scrollView.contentOffset.x == ScreenWidth * CGFloat(imageNames.count - 1) {
let cell = collectionView?.cellForItemAtIndexPath(NSIndexPath(forRow: imageNames.count - 1, inSection: 0)) as! GuideCollectionViewCell
cell.setNextBtnHidden(false)
isHiddenNextButton = false
}
}
当滑动即将停止的时候,通过判断滑动页面的x的偏移量来设置cell中Button的隐藏或者显示
func scrollViewDidScroll(scrollView: UIScrollView) {

        if scrollView.contentOffset.x != ScreenWidth * CGFloat(imageNames.count - 1) && !isHiddenNextButton && scrollView.contentOffset.x > ScreenWidth * CGFloat(imageNames.count - 2) {
let cell = collectionView?.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: NSIndexPath(forRow: imageNames.count - 1, inSection: 0)) as! GuideCollectionViewCell
cell.setNextBtnHidden(true)
isHiddenNextButton = true
}
pageController.currentPage = Int(scrollView.contentOffset.x / ScreenWidth + 0.5)
}
当页面开始滑动时判断综合条件对cell中button进行设置,并且设置pageControl的currentPage

至此实现了启动导航页的collectionView实现的方法

接下来说一下有动画效果的TabBarItem的实现

底部的TabBarItem动画使用了三方框架RAMAnimatedTabBar,由于原来的框架 只能通过StoryBoard初始化控件,并且无法满足项目需求,所以源作者对框架进行了修改,在TabBarController初始化时,通过拦截Items,重新创建一套相同的View,并且在每个View上添加ImageView和Label,在View的点击事件中,控制动画即可.代码如下:

1.这是声明了一个选择TabBarItem时动画的协议
protocol RAMItemAnimationProtocol {
func playAnimation(icon: UIImageView, textLabel:UILabel)
func deselectAnimation(icon: UIImageView, textLabel: UILabel, defaultTextColor: UIColor)
func selectedState(icon: UIImageView, textLabel: UILabel)
}
2.遵守动画协议创建的动画类,主要是设置动画周期,item中选择的颜色
class RAMItemAnimation:NSObject, RAMItemAnimationProtocol { var duration: CGFloat = 0.6
var textSelectedColor: UIColor = UIColor.grayColor()
var iconSelectedColor: UIColor? func playAnimation(icon: UIImageView, textLabel: UILabel) { } func deselectAnimation(icon: UIImageView, textLabel: UILabel, defaultTextColor: UIColor) { } func selectedState(icon: UIImageView, textLabel: UILabel) { }
}
继承自RAMItemAnimation的类,主要实现父类中的协议方法,具体设置textLabel和UIImageView的动画周期和效果
class RAMBounceAnimation: RAMItemAnimation { override func playAnimation(icon: UIImageView, textLabel: UILabel) { playBounceAnimation(icon)
textLabel.textColor = textSelectedColor
} override func deselectAnimation(icon: UIImageView, textLabel: UILabel, defaultTextColor: UIColor) { textLabel.textColor = defaultTextColor if let iconImage = icon.image {
let renderImage = iconImage.imageWithRenderingMode(.AlwaysOriginal)
icon.image = renderImage
icon.tintColor = defaultTextColor
}
} override func selectedState(icon: UIImageView, textLabel: UILabel) { textLabel.textColor = textSelectedColor if let iconImage = icon.image {
let renderImage = iconImage.imageWithRenderingMode(.AlwaysOriginal)
icon.image = renderImage
icon.tintColor = textSelectedColor
}
} func playBounceAnimation(icon: UIImageView) { let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
bounceAnimation.values = [1.0, 1.4, 0.9, 1.15, 0.95, 1.02, 1.0]
bounceAnimation.duration = NSTimeInterval(duration)
bounceAnimation.calculationMode = kCAAnimationCubic icon.layer.addAnimation(bounceAnimation, forKey: "bounceAnimation") if let iconImage = icon.image {
let renderImage = iconImage.imageWithRenderingMode(.AlwaysOriginal)
icon.image = renderImage
icon.tintColor = iconSelectedColor
}
}
}
自定义UITabBarItem,声明UITabBarItem中的func deselectAnimation(icon: UIImageView, textLabel: UILabel)和    func selectedState(icon:UIImageView, textLabel:UILabel)  方法,以便在自定义UITabBarController中使用,其中animation是继承的RAMItemAnimation,可以调用父类中的方法,也就是父类中提前已经声明好的几个动画函数

class RAMAnimatedTabBarItem: UITabBarItem {

    var animation: RAMItemAnimation?
var textColor = UIColor.grayColor() func playAnimation(icon: UIImageView, textLabel: UILabel) { guard let animation = animation else {
print("add animation in UITabBarItem")
return
}
animation.playAnimation(icon, textLabel: textLabel)
} func deselectAnimation(icon: UIImageView, textLabel: UILabel) { animation?.deselectAnimation(icon, textLabel: textLabel, defaultTextColor: textColor)
} func selectedState(icon:UIImageView, textLabel:UILabel) {
animation?.selectedState(icon , textLabel: textLabel)
} }
class AnimationTabBarController: UITabBarController {

    var iconsView:[(icon: UIImageView, textLabel: UILabel)] = []
var iconsImageName:[String] = ["v2_home", "v2_order", "shopCart", "v2_my"]
var iconsSelectedImageName:[String] = ["v2_home_r", "v2_order_r", "shopCart_r", "v2_my_r"] override func viewDidLoad() {
super.viewDidLoad() // Do any additional setup after loading the view.
} //创建承载TabBarItem的视图容器,里面是item中的titleLabel和UIImageView,将视图容器存入字典,以便在后续使用中可以分清是点了哪一个item func createViewContainers() -> [String: UIView] { var containersDict = [String: UIView]()
//tabbar的item是继承的自定义的RAMAnimatedTabBarItem,其中包含了动画设置的函数
guard let customItems = tabBar.items as? [RAMAnimatedTabBarItem]
else {
return containersDict
}
//根据item的个数创建视图容器,将视图容器放在字典中
for index in 0..<customItems.count {
let viewContainer = createViewContainer(index)
containersDict["container\(index)"] = viewContainer
}
return containersDict
} //根据index值创建每个的视图容器
func createViewContainer(index: Int) -> UIView { let viewWidth: CGFloat = ScreenWidth / CGFloat(tabBar.items!.count)
let viewHeight: CGFloat = tabBar.height
let viewContainer = UIView(frame: CGRectMake(viewWidth * CGFloat(index), 0, viewWidth, viewHeight)) viewContainer.backgroundColor = UIColor.clearColor()
viewContainer.userInteractionEnabled = true tabBar.addSubview(viewContainer)
viewContainer.tag = index //给容器添加手势,其实是自己重写了系统的item的功能,因为我们要在里面加入动画
let tap = UITapGestureRecognizer(target: self, action: "tabBarClick:")
viewContainer.addGestureRecognizer(tap)
return viewContainer } //创建items的具体内容
func createCustomIcons(containers: [String: UIView]) { if let items = tabBar.items {
for (index, item) in items.enumerate() { assert(item.image != nil, "add image icon in UITabBarItem")
guard let container = containers["container\(index)"] else {
print("No container given")
continue
}
container.tag = index let imageW:CGFloat = 21
let imageX:CGFloat = (ScreenWidth / CGFloat(items.count) - imageW) * 0.5
let imageY:CGFloat = 8
let imageH:CGFloat = 21
let icon = UIImageView(frame: CGRect(x: imageX, y: imageY, width: imageW, height: imageH))
icon.image = item.image
icon.tintColor = UIColor.clearColor() let textLabel = UILabel ()
textLabel.frame = CGRectMake(0, 32, ScreenWidth / CGFloat(items.count), 49 - 32)
textLabel.text = item.title
textLabel.backgroundColor = UIColor.clearColor()
textLabel.font = UIFont.systemFontOfSize(10)
textLabel.textAlignment = NSTextAlignment.Center
textLabel.textColor = UIColor.grayColor()
textLabel.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(icon)
container.addSubview(textLabel) if let tabBarItem = tabBar.items {
let textLabelWidth = tabBar.frame.size.width / CGFloat(tabBarItem.count)
textLabel.bounds.size.width = textLabelWidth
} let iconsAndLabels = (icon:icon, textLabel:textLabel)
iconsView.append(iconsAndLabels) item.image = nil
item.title = "" if index == 0 {
selectedIndex = 0
selectItem(0)
}
}
}
} //重写父类的didSelectItem
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
setSelectIndex(from: selectedIndex, to: item.tag)
} //选择item时item中内容的变化
func selectItem(index: Int) {
let items = tabBar.items as! [RAMAnimatedTabBarItem]
let selectIcon = iconsView[index].icon
selectIcon.image = UIImage(named: iconsSelectedImageName[index])!
items[index].selectedState(selectIcon, textLabel: iconsView[index].textLabel)
} //根据选择的index值设置item中的内容并且执行动画父类中的方法
func setSelectIndex(from from: Int, to: Int) { selectedIndex = to
let items = tabBar.items as! [RAMAnimatedTabBarItem] let fromIV = iconsView[from].icon
fromIV.image = UIImage(named: iconsImageName[from])
items[from].deselectAnimation(fromIV, textLabel: iconsView[from].textLabel) let toIV = iconsView[to].icon
toIV.image = UIImage(named: iconsSelectedImageName[to])
items[to].playAnimation(toIV, textLabel: iconsView[to].textLabel)
} override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
} }
最后MainTabBarController继承AnimationTabBarController

调用createViewContainers也就是父类中的方法

并且给tabBarController添加视图控制器

//MARK: - 初始化tabbar
private func createMainTabBarChildViewController() {
tabBarControllerAddChildViewController(HomeViewController(), title: "首页", imageName: "v2_home", selectedImageName: "v2_home_r", tag: 0)
tabBarControllerAddChildViewController(MarketViewController(), title: "超市", imageName: "v2_order", selectedImageName: "v2_order_r", tag: 1)
tabBarControllerAddChildViewController(ShopViewController(), title: "商店", imageName: "shopCart", selectedImageName: "shopCart", tag: 2)
tabBarControllerAddChildViewController(MineViewController(), title: "我的", imageName: "v2_my", selectedImageName: "v2_my_r", tag: 3) } private func tabBarControllerAddChildViewController(childView: UIViewController, title: String, imageName: String, selectedImageName: String, tag: Int) { let vcItem = RAMAnimatedTabBarItem(title: title, image: UIImage(named: imageName), selectedImage: UIImage(named: selectedImageName))
vcItem.tag = tag
vcItem.animation = RAMBounceAnimation()
childView.tabBarItem = vcItem let navigationVC = BaseNavigationController(rootViewController:childView)
addChildViewController(navigationVC)
} func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
let childArr = tabBarController.childViewControllers as NSArray
let index = childArr.indexOfObject(viewController) if index == 2 {
return false
}
return true
}

这只是原作者项目中一个比较小的部分,原作者链接:http://www.jianshu.com/p/879f58fe3542

本Demo链接:https://github.com/iOSJason/AnimationTabBarItemSwiftDemo.git

Swift 带有动画效果的TabBarItem的更多相关文章

  1. iOS开发——动画篇Swift篇&动画效果的实现

    Swift - 动画效果的实现   在iOS中,实现动画有两种方法.一个是统一的animateWithDuration,另一个是组合出现的beginAnimations和commitAnimation ...

  2. hover带有动画效果的导航

    html,body{overflow-x:hidden;} ul,li{list-style: none;} .nav{width:100%; height: 26px; overflow: hidd ...

  3. anime.js 实战:实现一个带有描边动画效果的复选框

    在网页或者是APP的开发中,动画运用得当可以起到锦上添花的作用.正确使用动画,不但可以有助于用户理解交互的作用,还可以大大提高网页应用的魅力和使用体验.并且在现在的网页开发中,动画已经成为了一个设计的 ...

  4. iOS 开源一个高度可定制支持各种动画效果,支持单击双击,小红点,支持自定义不规则按钮的tabbar

    TYTabbarAnimationDemo 业务需求导致需要做一个tabbar,里面的按钮点击带有动画效果,tabbar中间的按钮凸出,凸出部分可以点击,支持badge 小红点等,为此封装了一个高度可 ...

  5. 自定义PopupWindow弹出框(带有动画)

    使用PopupWindow来实现弹出框,并且带有动画效果 首先自定义PopupWindow public class LostPopupWindow extends PopupWindow { pub ...

  6. 一个带动画效果的颜色选择对话框控件AnimatedColorPickerDialog

    android4.4的日历中选择日程显示颜色的时候有一个颜色选择对话框非常漂亮,模仿他的界面我实现了一个类似的对话框,而且带有动画效果. 代码的实现可讲的地方不多,主要是采用了和AlertDialog ...

  7. Swift - 多个mask的动画效果

    Swift - 多个mask的动画效果 效果 源码 https://github.com/YouXianMing/Swift-Animations // // TranformFadeView.swi ...

  8. Swift - 用UIScrollView实现视差动画效果

    Swift - 用UIScrollView实现视差动画效果 效果 源码 https://github.com/YouXianMing/Swift-Animations // // MoreInfoVi ...

  9. [Swift通天遁地]五、高级扩展-(11)图像加载Loading动画效果的自定义和缓存

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

随机推荐

  1. kill&&pkill&&killall---删除执行中的程序

    命令功能: 发送指定的信号到相应进程.不指定型号将发送SIGTERM(15)终止指定进程.如果无法终止该程序可用“-KILL” 参数,其发送的信号为SIGKILL(9) ,将强制结束进程 使用ps命令 ...

  2. 小米开源文件管理器MiCodeFileExplorer-源码研究(9)-入口分析

    AndroidManifest.xml是Android应用程序最重要的配置文件. 入口文件和intent-filter <application android:icon="@draw ...

  3. UVA - 10674-Tangents

     题意:给出两个圆,求它们的公切线,并依照一定格式输出 做法:模拟 代码: #include<iostream> #include<map> #include<str ...

  4. 初步使用RecyclerView实现瀑布流

    先看效果 关于RecyclerView,真的是很强大. 个人觉得主要方便的地方是 1.直接可以设置条目布局,通过setLayoutManager LinearLayoutManager:线性布局,横向 ...

  5. pytest_多用例执行(1)

    一.首先创建测试套件 # -*- coding:utf-8 -*-from __future__ import print_functionimport pytestimport allure cla ...

  6. 百度地图API 添加标签

    1.手动创建数据,实际项目则是接受GPS信息 /建立坐标点: // lng:经度 lat:纬度 var points = [ {"lng":112.58,"lat&quo ...

  7. C# 反射基础运用

    反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型 (包括类.结构.委托.接口和枚举等)的成员和成员的信息. 其实有些人会问, 像我们平常引用DLL可以直接用, 为什么要用 ...

  8. 关于java中String的一点理解

      String类是java的最基本类之中的一个,非常好的掌握它的原理非常是必要的!   1.String的Final类型的.是不可继承 的.final类默认的方法都为final类型,保证了方法不能被 ...

  9. RocketMQ集群消费的那些事

    说明 RocketMQ集群消费的时候,我们经常看到类似注释里面 (1,(2 的写法,已经有时候有同学没注意抛异常的情况就是(3 模拟的情况.那么这3种情况到底是怎么样的呢?你是否都了然于心呢?下面我们 ...

  10. Windows Route 路由表命令整理

    Windows Route 路由表命令 在本地 IP 路由表中显示和修改条目. 语法 route [-f] [-p] [Command [Destination] [mask Netmask] [Ga ...