I am trying to create a animation like of Facebook Menu Slide Down Animation of POP framework or exactly like of InShorts App. Android Documentation covers this..But cannot
This effect can be obtained using UICollectionView
.Here is the UICollectionViewFlowLayout
class.
class UltravisualLayout: UICollectionViewLayout {
private var contentWidth:CGFloat!
private var contentHeight:CGFloat!
private var yOffset:CGFloat = 0
var maxAlpha:CGFloat = 1
var minAlpha:CGFloat = 0
var widthOffset:CGFloat = 35
var heightOffset:CGFloat = 35
private var cache = [UICollectionViewLayoutAttributes]()
private var itemWidth:CGFloat{
return (collectionView?.bounds.width)!
}
private var itemHeight:CGFloat{
return (collectionView?.bounds.height)!
}
private var collectionViewHeight:CGFloat{
return (collectionView?.bounds.height)!
}
private var numberOfItems:Int{
return (collectionView?.numberOfItemsInSection(0))!
}
private var dragOffset:CGFloat{
return (collectionView?.bounds.height)!
}
private var currentItemIndex:Int{
return max(0, Int(collectionView!.contentOffset.y / collectionViewHeight))
}
var nextItemBecomeCurrentPercentage:CGFloat{
return (collectionView!.contentOffset.y / (collectionViewHeight)) - CGFloat(currentItemIndex)
}
override func prepareLayout() {
cache.removeAll(keepCapacity: false)
yOffset = 0
for item in 0 ..< numberOfItems{
let indexPath = NSIndexPath(forItem: item, inSection: 0)
let attribute = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
attribute.zIndex = -indexPath.row
if (indexPath.item == currentItemIndex+1) && (indexPath.item < numberOfItems){
attribute.alpha = minAlpha + max((maxAlpha-minAlpha) * nextItemBecomeCurrentPercentage, 0)
let width = itemWidth - widthOffset + (widthOffset * nextItemBecomeCurrentPercentage)
let height = itemHeight - heightOffset + (heightOffset * nextItemBecomeCurrentPercentage)
let deltaWidth = width/itemWidth
let deltaHeight = height/itemHeight
attribute.frame = CGRectMake(0, yOffset, itemWidth, itemHeight)
attribute.transform = CGAffineTransformMakeScale(deltaWidth, deltaHeight)
attribute.center.y = (collectionView?.center.y)! + (collectionView?.contentOffset.y)!
attribute.center.x = (collectionView?.center.x)! + (collectionView?.contentOffset.x)!
yOffset += collectionViewHeight
}else{
attribute.frame = CGRectMake(0, yOffset, itemWidth, itemHeight)
attribute.center.y = (collectionView?.center.y)! + yOffset
attribute.center.x = (collectionView?.center.x)!
yOffset += collectionViewHeight
}
cache.append(attribute)
}
}
//Return the size of ContentView
override func collectionViewContentSize() -> CGSize {
contentWidth = (collectionView?.bounds.width)!
contentHeight = CGFloat(numberOfItems) * (collectionView?.bounds.height)!
return CGSizeMake(contentWidth, contentHeight)
}
//Return Attributes whose frame lies in the Visible Rect
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attribute in cache{
if CGRectIntersectsRect(attribute.frame, rect){
layoutAttributes.append(attribute)
}
}
return layoutAttributes
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
let itemIndex = round(proposedContentOffset.y / (dragOffset))
let yOffset = itemIndex * (collectionView?.bounds.height)!
return CGPoint(x: 0, y: yOffset)
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
// Logic that calculates the UICollectionViewLayoutAttributes of the item
// and returns the UICollectionViewLayoutAttributes
return UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
}
}
This is the demo Project link...
Great thanks to this tutorial.
Use facebook POP animation For drop dragged view to resting with animation
private func resetViewPositionAndTransformations() {
let resetPositionAnimation = POPSpringAnimation(propertyNamed: kPOPLayerTranslationXY)
resetPositionAnimation.fromValue = NSValue(CGPoint: CGPoint(x: dragDistance.x, y: dragDistance.y))
resetPositionAnimation.toValue = NSValue(CGPoint: CGPointZero)
resetPositionAnimation.springBounciness = cardResetAnimationSpringBounciness
resetPositionAnimation.springSpeed = cardResetAnimationSpringSpeed
resetPositionAnimation.completionBlock = {
(_, _) in
self.layer.transform = CATransform3DIdentity
}
layer.pop_addAnimation(resetPositionAnimation, forKey: "resetPositionAnimation")
let resetRotationAnimation = POPBasicAnimation(propertyNamed: kPOPLayerRotation)
resetRotationAnimation.fromValue = POPLayerGetRotationZ(layer)
resetRotationAnimation.toValue = CGFloat(0.0)
resetRotationAnimation.duration = cardResetAnimationDuration
layer.pop_addAnimation(resetRotationAnimation, forKey: "resetRotationAnimation")
let overlayAlphaAnimation = POPBasicAnimation(propertyNamed: kPOPViewAlpha)
overlayAlphaAnimation.toValue = 0.0
overlayAlphaAnimation.duration = cardResetAnimationDuration
overlayAlphaAnimation.completionBlock = { _, _ in
}
overlayView?.pop_addAnimation(overlayAlphaAnimation, forKey: "resetOverlayAnimation")
let resetScaleAnimation = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
resetScaleAnimation.toValue = NSValue(CGPoint: CGPoint(x: 1.0, y: 1.0))
resetScaleAnimation.duration = cardResetAnimationDuration
layer.pop_addAnimation(resetScaleAnimation, forKey: "resetScaleAnimation")
}
if you want exactly what the InShorts video is doing i'd recommend using view controller transitions. a helpful article is here.
full example code of what you are trying to achieve is below. i implemented it all in the app delegate for brevity, but you should definitely break it out into separate classes.
Video: here
UPDATED CODE
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UINavigationControllerDelegate, UIViewControllerAnimatedTransitioning {
var window: UIWindow?
var percentManager: UIPercentDrivenInteractiveTransition?
/**
Keep track of which way the animation is going
*/
var isPopping = false
/**
The index of the view controller that is currently presented.
*/
var index: Int {
get {
guard let lastVC = navController.viewControllers.last as? ViewController else { return NSNotFound }
return lastVC.index
}
}
/**
Acting as a very basic data source.
- Note: When this value is set the navigation stack is reset and the first VC is created
*/
var count: Int = 0 {
didSet {
guard count > 0 else { return }
// load the first view controller
navController.setViewControllers([ViewController(index: 0)], animated: true)
}
}
lazy var navController: UINavigationController = {
// create a navigation controller and hide it's nav bar
let navController = UINavigationController()
navController.navigationBar.hidden = true
return navController
}()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// add the nav controller (and it's first view controller) to the window
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
// add a pan recognizer to the nav controller
let panRecognizer = UIPanGestureRecognizer(target: self, action: Selector("didPan:"))
navController.view.addGestureRecognizer(panRecognizer)
navController.delegate = self
// update the 'data source'
count = 5
return true
}
internal func didPan(recognizer: UIPanGestureRecognizer) {
guard let view = recognizer.view else { assertionFailure("no view"); return }
switch (recognizer.state) {
case .Began:
// detect if it is an upward swipe
if recognizer.velocityInView(view).y < 0 {
// don't go out of bounds
guard index + 1 < count else { recognizer.enabled = false; recognizer.enabled = true; return }
isPopping = false
// create the percentManager and start pushing the next view controller
percentManager = UIPercentDrivenInteractiveTransition()
navController.pushViewController(ViewController(index: index+1), animated: true)
}
// detect if it is a downward swipe
else if recognizer.velocityInView(view).y > 0 {
// don't go out of bounds
guard index - 1 >= 0 else { recognizer.enabled = false; recognizer.enabled = true; return }
isPopping = true
// create the percentManager and start popping the current view controller
percentManager = UIPercentDrivenInteractiveTransition()
navController.popViewControllerAnimated(true)
}
case .Changed:
// update the percent manager
let translation = recognizer.translationInView(view)
let percentOffset = translation.y/CGRectGetHeight(view.bounds) * (isPopping ? 1 : -1)
percentManager?.updateInteractiveTransition(percentOffset)
case .Ended:
// give the percent manager it's final instruction before niling it
if isPopping {
if recognizer.velocityInView(view).y > 0 {
percentManager?.finishInteractiveTransition()
} else {
percentManager?.cancelInteractiveTransition()
}
} else {
if recognizer.velocityInView(view).y < 0 {
percentManager?.finishInteractiveTransition()
} else {
percentManager?.cancelInteractiveTransition()
}
}
percentManager = nil
default:
break
}
}
// MARK: UINavigationControllerDelegate
func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return percentManager
}
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
// MARK: UIViewControllerAnimatedTransitioning
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
guard let newVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as? ViewController else { assertionFailure("no new view controller"); return }
guard let oldVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as? ViewController else { assertionFailure("no existing view controller"); return }
let scaleTransform = CGAffineTransformMakeScale(0.9, 0.9)
let yOffsetTransform = CGAffineTransformMakeTranslation(0, -CGRectGetHeight(oldVC.view.frame))
let isPopping = newVC.index < oldVC.index
let animationBlock: Void -> Void
if isPopping == true {
// place the previous vc at the top of the screen
newVC.view.transform = yOffsetTransform
animationBlock = {
oldVC.view.transform = scaleTransform
newVC.view.transform = CGAffineTransformIdentity
}
// add the views onto the transition view controller
transitionContext.containerView()?.addSubview(oldVC.view)
transitionContext.containerView()?.addSubview(newVC.view)
} else {
// scale the new view a bit smaller
newVC.view.transform = scaleTransform
// slide the old view controller up and scale the new view controller back to 100%
animationBlock = {
oldVC.view.transform = yOffsetTransform
newVC.view.transform = CGAffineTransformIdentity
}
// add the views onto the transition view controller
transitionContext.containerView()?.addSubview(newVC.view)
transitionContext.containerView()?.addSubview(oldVC.view)
}
// perform the animation
UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: animationBlock, completion: { finished in
// cleanup
oldVC.view.transform = CGAffineTransformIdentity
newVC.view.transform = CGAffineTransformIdentity
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
})
}
}
class ViewController: UIViewController {
let index: Int
init(index: Int) {
self.index = index
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: CGFloat((arc4random()%255))/255.0, green: CGFloat((arc4random()%255))/255.0, blue: CGFloat((arc4random()%255))/255.0, alpha: 1)
view.layer.cornerRadius = 12
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Based on your video I sorted out what kind of animation you want to do. So my approach this time
self.view
), and implement the 3 states of pan touchThis code has simple views, which change their background corresponding to the number of colors that we have on color array. So in your case you can create your custom XIB View, and also instead of colors you can add your own datasource. :)
import UIKit
class StackedViewController: UIViewController {
var previousView: UIView! = nil
var currentView: UIView! = nil
var nextView: UIView! = nil
var currentIndex = 0
let colors: [UIColor] = [.redColor(), .greenColor(), .yellowColor(), .grayColor()]
var offset: CGFloat = CGFloat()
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Set the offset
offset = 64.0
setupViews()
}
// MARK: - Setups
func setupViews() {
self.view.backgroundColor = .blackColor()
self.currentView = getCurrentView()
self.view.addSubview(currentView)
let pan = UIPanGestureRecognizer(target: self, action: "panAction:")
self.view.addGestureRecognizer(pan)
}
// MARK: - Actions
func panAction(gesture:UIPanGestureRecognizer){
let p = gesture.translationInView(self.view)
// Edge cases to disable panning when the view stack is finished
if p.y < 0 && getPreviousView() == nil || p.y > 0 && getNextView() == nil {
return
}
if gesture.state == .Began {
if let prev = getPreviousView() {
self.previousView = prev
self.view.addSubview(prev)
prev.frame = self.view.frame
prev.center = CGPointMake(self.view.bounds.width/2, self.view.bounds.height/2)
self.view.sendSubviewToBack(previousView)
}
if let next = getNextView() {
self.nextView = next
self.view.addSubview(next)
next.frame = CGRect(origin: CGPoint(x: 0, y: -self.view.bounds.height), size: self.view.frame.size)
}
} else if gesture.state == .Changed {
UIView.animateWithDuration(0.1, animations: { () -> Void in
if p.y < 0 {
self.previousView.hidden = false
self.currentView.center.y = self.view.bounds.height/2 + p.y
self.previousView.center.y = self.view.bounds.height/2
// Transforming ratio from 0-1 to 0.9-1
let ratio = (-p.y/CGRectGetHeight(self.view.bounds))
let lightRatio = 0.9 + (ratio/10)
// Apply transformation
self.apply3DDepthTransform(self.previousView, ratio: lightRatio)
} else if p.y > 0 {
self.currentView.center.y = self.view.bounds.height/2
let prevPosY = -self.view.bounds.height/2 + p.y
if prevPosY < self.view.bounds.height/2 {
self.nextView?.center.y = prevPosY
} else {
self.nextView?.center.y = self.view.bounds.height/2
}
// Transforming ratio from 0-1 to 0.9-1
let ratio = p.y/CGRectGetHeight(self.view.bounds)
let lightRatio = 1 - (ratio/10)
// Apply transformation
self.apply3DDepthTransform(self.currentView, ratio: lightRatio)
// Hide the background view when showing another because the edges of this will be shown due to transformation
if self.previousView != nil {
self.previousView.hidden = true
}
}
})
} else if gesture.state == .Ended {
UIView.animateWithDuration(0.5, delay: 0,
options: [.CurveEaseOut], animations: { () -> Void in
if p.y < -self.offset && self.previousView != nil {
// Showing previous item
self.currentView.center.y = -self.view.bounds.height/2
// Finish the whole transition
self.apply3DDepthTransform(self.previousView, ratio: 1)
} else if p.y > self.offset && self.nextView != nil {
// Showing next item
self.nextView?.center.y = self.view.bounds.height/2
// Finish the whole transition
self.apply3DDepthTransform(self.currentView, ratio: 0.9)
} else {
// The pan has not passed offset so just return to the main coordinates
self.previousView?.center.y = -self.view.bounds.height/2
self.currentView.center.y = self.view.bounds.height/2
}
}, completion: { (_) -> Void in
if p.y < -self.offset && self.previousView != nil {
self.currentView = self.getPreviousView()
self.currentIndex = (self.currentIndex > 0) ? self.currentIndex - 1 : self.currentIndex;
} else if p.y > self.offset && self.nextView != nil {
self.currentView = self.getNextView()
self.currentIndex = (self.currentIndex == self.colors.count - 1) ? self.currentIndex : self.currentIndex + 1;
}
// Remove all views and show the currentView
for view in self.view.subviews {
view.removeFromSuperview()
}
self.previousView = nil
self.nextView = nil
self.view.addSubview(self.currentView)
self.currentView.center = CGPointMake(self.view.bounds.width/2, self.view.bounds.height/2)
})
}
}
// MARK: - Helpers
func getCurrentView() -> UIView? {
let current = UIView(frame: self.view.frame)
current.backgroundColor = colors[currentIndex]
return current
}
func getNextView() -> UIView? {
if currentIndex >= colors.count - 1 {
return nil
}
let next = UIView(frame: self.view.frame)
next.backgroundColor = colors[currentIndex + 1]
return next
}
func getPreviousView() -> UIView? {
if currentIndex <= 0 {
return nil
}
let prev = UIView(frame: self.view.frame)
prev.backgroundColor = colors[currentIndex - 1]
return prev
}
// MARK: Animation
func apply3DDepthTransform(view: UIView, ratio: CGFloat) {
view.layer.transform = CATransform3DMakeScale(ratio, ratio, ratio)
view.alpha = 1 - ((1 - ratio)*10)
}
}
You can do it only with need of the cells, without having to make custom transition with UIViewController
Solution consists on:
scrollViewDidScroll:
which coordinates the whole operation, because everything is dependent on scrollingOffset
ViewController
. Please clear existing constraints, fill the view with the tableView and then let it be 0px from the Top (Not Top Layout Guide).No more talking, code talks itself (it is also some commented)
import UIKit
enum ViewControllerScrollDirection: Int {
case Up
case Down
case None
}
class ViewController: UIViewController {
@IBOutlet weak var menuTableView: UITableView!
var colors :[UIColor] = [UIColor.greenColor(),UIColor.grayColor(),UIColor.purpleColor(),UIColor.redColor()]
var presenterCell: UITableViewCell! = nil
var isBeingPresentedCell: UITableViewCell! = nil
var lastContentOffset: CGFloat = CGFloat()
var scrollDirection: ViewControllerScrollDirection = .None
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
menuTableView.dataSource = self
menuTableView.delegate = self
menuTableView.pagingEnabled = true
}
}
extension ViewController:UITableViewDataSource,UITableViewDelegate {
// MARK: - Delegation
// MARK: TableView Datasource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.contentView.backgroundColor = colors[indexPath.row]
cell.backgroundColor = UIColor.blackColor()
cell.contentView.layer.transform = CATransform3DIdentity
cell.selectionStyle = .None
return cell
}
// MARK: TableView Delegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return self.view.frame.size.height
}
// MARK: ScrollView Delegate
func scrollViewDidScroll(scrollView: UIScrollView) {
self.scrollDirection = getScrollDirection(scrollView)
// The cells in visible cells are ordered, so depending on scroll we set the one that we want to present
if self.scrollDirection == .Up {
self.presenterCell = menuTableView.visibleCells.last
self.isBeingPresentedCell = menuTableView.visibleCells.first
} else {
self.presenterCell = menuTableView.visibleCells.first
self.isBeingPresentedCell = menuTableView.visibleCells.last
}
// If we have the same cells or either of them is nil don't do anything
if (self.isBeingPresentedCell == nil || self.presenterCell == nil) {
return;
} else if (self.isBeingPresentedCell == self.presenterCell) {
return;
}
// Always animate the presenter cell to the identity (fixes the problem when changing direction on pan gesture)
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.presenterCell.contentView.layer.transform = CATransform3DIdentity;
})
// Get the indexPath
guard let indexPath = menuTableView.indexPathForCell(presenterCell) else {
return;
}
// Get the frame for that indexPath
let frame = menuTableView.rectForRowAtIndexPath(indexPath)
// Find how much vertical space is the isBeingPresented cell using on the frame and return always the positive value
var diffY = frame.origin.y - self.lastContentOffset
diffY = (diffY > 0) ? diffY : -diffY
// Find the ratio from 0-1 which corresponds on transformation from 0.8-1
var ratio = CGFloat(diffY/CGRectGetHeight(self.menuTableView.frame))
ratio = 0.8 + (ratio/5)
// Make the animation
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.isBeingPresentedCell.contentView.layer.transform = CATransform3DMakeScale(ratio, ratio, ratio)
})
}
// MARK: - Helpers
func getScrollDirection(scrollView: UIScrollView) -> ViewControllerScrollDirection {
let scrollDirection = (self.lastContentOffset > scrollView.contentOffset.y) ? ViewControllerScrollDirection.Down : ViewControllerScrollDirection.Up
self.lastContentOffset = scrollView.contentOffset.y;
return scrollDirection
}
}
Hope it will fill your requirements :)
in my opinion UICollectionview
and UICollectionViewLayout
is better suited for such things. UITableView
don't give you per scroll transforms and you would need to do all kinds dirty hacks.
If you go with UICollectionview
, what you looking for is -[UICollectionViewLayout layoutAttributesForElementsInRect:]
and -[UICollectionViewLayout shouldInvalidateLayoutForBoundsChange:]
, everything else is a bit of math.
Try this: You can us facebook/pop https://github.com/facebook/pop for smoother effect. This code is just to give you some idea, not tested much.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")
if cell?.contentView.viewWithTag(100) == nil
{
let view = UIView(frame: (cell?.contentView.bounds)!)
view.tag = 100
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
cell?.contentView.addSubview(view)
}
if cell!.contentView.viewWithTag(100) != nil
{
let view = cell!.contentView.viewWithTag(100)
view!.backgroundColor = colors[indexPath.row]
}
return cell!
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return self.view.frame.size.height - 20
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if cell.contentView.viewWithTag(100) != nil
{
let view = cell.contentView.viewWithTag(100)
let basicAnimation = CABasicAnimation(keyPath: "transform.scale")
basicAnimation.toValue = NSNumber(float: 1)
basicAnimation.fromValue = NSNumber(float: 0.8)
basicAnimation.duration = 0.4
basicAnimation.removedOnCompletion = false
view!.layer.addAnimation(basicAnimation, forKey: "transform.scale")
}
// if tableView.cellForRowAtIndexPath(NSIndexPath(forRow: indexPath.row - 1, inSection: 0)) != nil
// {
// let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: indexPath.row - 1, inSection: 0))
// if cell!.contentView.viewWithTag(100) != nil
// {
// let view = cell!.contentView.viewWithTag(100)
// let basicAnimation = CABasicAnimation(keyPath: "transform.scale")
// basicAnimation.toValue = NSNumber(float: 0.8)
// basicAnimation.fromValue = NSNumber(float: 1)
// basicAnimation.duration = 0.4
// basicAnimation.removedOnCompletion = false
// view!.layer.addAnimation(basicAnimation, forKey: "transform.scale")
// }
// }
// if tableView.cellForRowAtIndexPath(NSIndexPath(forRow: indexPath.row + 1, inSection: 0)) != nil
// {
// let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: indexPath.row + 1, inSection: 0))
//
// if cell!.contentView.viewWithTag(100) != nil
// {
// let view = cell!.contentView.viewWithTag(100)
// let basicAnimation = CABasicAnimation(keyPath: "transform.scale")
// basicAnimation.toValue = NSNumber(float: 0.8)
// basicAnimation.fromValue = NSNumber(float: 1)
// basicAnimation.duration = 0.4
// basicAnimation.removedOnCompletion = false
// view!.layer.addAnimation(basicAnimation, forKey: "transform.scale")
// }
// }
// let scaleAnimation = POPDecayAnimation(propertyNamed: kPOPLayerScaleXY)
// scaleAnimation.fromValue = NSValue(CGSize: CGSize(width: 0.8, height: 0.8))
// scaleAnimation.toValue = NSValue(CGSize: CGSize(width: 1, height: 1))
// cell.contentView.layer.pop_addAnimation(scaleAnimation, forKey: "kPOPLayerScaleXY")
// UIView.animateWithDuration(0.1, animations: { () -> Void in
// cell.transform = CGAffineTransformMakeScale(0.8, 0.8)
// })
}