iOS上的深度页面转换

时间:2016-03-01 08:17:58

标签: ios swift swift2 uitableview

我正在尝试创建一个类似于POP框架的Facebook菜单滑动动画或类似于InShorts App的动画。 Android Documentation涵盖了这一点。但是在iOS中找不到任何提示。

我尝试的是将细胞转换为

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    UIView.animateWithDuration(0.1, animations: { () -> Void in
        cell.transform = CGAffineTransformMakeScale(0.8, 0.8)
    })

}

但是这不能按预期工作.Google搜索我发现this正是我想要实现的目标。但它在UIView上。我认为如果我们在UITableViewCell中制作动画最合适吗?我真的很感激有人愿意帮助解决这个问题。 Here是我正在开展的初学项目

6 个答案:

答案 0 :(得分:5)

如果你想要InShorts视频正在做什么,我建议使用view controller transitions。一篇有用的文章是here

您要尝试实现的完整示例代码如下。为简洁起见,我在app委托中实现了所有这些,但你绝对应该把它分成不同的类。

视频:here

更新代码

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

答案 1 :(得分:5)

可以使用UICollectionView获得此效果。这是UICollectionViewFlowLayout类。

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

}

这是演示项目link ...

非常感谢tutorial

答案 2 :(得分:4)

更新

根据您的视频,我整理出您想要制作的动画类型。所以这次我的方法

  1. 使用3个视图引用,上一个当前下一个
  2. 将平移手势添加到保存这些视图的视图(当前 self.view),并实现pan touch的3种状态
  3. 根据手势记录应用转换,并排列视图
  4. 有些学分在GitHub上去了这个图书馆,这对我帮助很大。
  5. 代码

    此代码具有简单的视图,可以根据颜色数组上的颜色数更改背景。因此,在您的情况下,您可以创建自定义XIB视图,而不是颜色,您可以添加自己的数据源。 :)

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

    输出

    enter image description here

    原始答案

    您只能根据细胞的需要进行操作,而无需使用UIViewController进行自定义转换

    解决方案

    解决方案包括:

    1. 跟踪2个单元格,即演示者( presenterCell )和将要呈现的单元格( isBeingPresentedCell
    2. 实施协调整个操作的scrollViewDidScroll:,因为一切都依赖于scrollingOffset
    3. 处理滚动的方向以选择哪个单元格是演示者,哪个单元格正在呈现
    4. 每当一个单元格将被出列时,它将被设置一个标识转换,因为在正常屏幕上我们只有一个单元格显示
    5. 保持覆盖整个框架的矩形比例因子,以便在转换时应用正确的因子
    6. **** ViewController上的 Autolayout 存在问题。请清除现有约束,使用tableView填充视图,然后从顶部(不是顶部布局指南)中将其设置为0px。
    7. 代码

      不再说话,代码自言自语(也有一些评论)

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

      UI修复

      enter image description here

      输出动画

      enter image description here

      希望它能满足您的要求:)

答案 3 :(得分:2)

使用facebook POP动画将拖动视图拖放到动画

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

答案 4 :(得分:0)

在我看来,{p> UICollectionviewUICollectionViewLayout更适合这类事情。 UITableView不会为每个滚动转换提供给你,你需要做各种肮脏的黑客攻击。

如果你选择UICollectionview,你要找的是-[UICollectionViewLayout layoutAttributesForElementsInRect:]-[UICollectionViewLayout shouldInvalidateLayoutForBoundsChange:],其他一切都是数学上的。

答案 5 :(得分:-1)

试试这个: 你可以通过facebook / pop https://github.com/facebook/pop获得更流畅的效果。 这段代码只是为了给你一些想法,而不是经过多次测试。

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


}