簡體   English   中英

iOS上的深度頁面轉換

[英]Depth Page transform on iOS

我正在嘗試創建一個類似於POP菜單的Facebook菜單滑動動畫或類似於InShorts App的動畫。 Android文檔涵蓋了這個..但是在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)
    })

}

但這並沒有像預期的那樣工作。谷歌搜索我發現正是我想要達到的目標。 但它在UIView 我認為如果我們在UITableViewCell制作動畫最合適嗎? 我真的很感激有人願意幫助解決這個問題。 是我正在開發的入門項目

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

}

這是演示項目鏈接 ......

非常感謝本教程

如果您想要InShorts視頻正在做什么,我建議使用視圖控制器轉換 一篇有用的文章就在這里

您要實現的完整示例代碼如下所示。 為了簡潔起見,我在app委托中實現了所有這些,但你絕對應該將它分解為單獨的類。

視頻: 這里

更新的代碼

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. 使用3個視圖引用,分別是上一個當前下一個
  2. 將平移手勢添加到持有這些視圖的視圖(當前為self.view ),並實現平移觸摸的3種狀態
  3. 根據手勢記錄應用轉換,並排列視圖
  4. 一些學分在GitHub上訪問這個庫,這對我幫助很大。

此代碼具有簡單視圖,這些視圖會根據顏色數組上的顏色數更改其背景。 因此,在您的情況下,您可以創建自定義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)
    }
}

產量

在此輸入圖像描述

原始答案

您只能在需要單元格的情況下執行此操作,而無需使用UIViewController進行自定義轉換

解決方案包括:

  1. 跟蹤2個單元格,即演示者( presenterCell )和將要呈現的單元格( isBeingPresentedCell
  2. 實現scrollViewDidScroll:它協調整個操作,因為一切都依賴於scrollingOffset
  3. 處理滾動的方向,選擇哪個單元格是演示者,哪個單元格正在呈現
  4. 每當一個單元格將被出列時,它將被設置一個標識轉換,因為在正常屏幕上我們只有一個單元格顯示
  5. 保持覆蓋整個框架的矩形的比率因子,以便在轉換時應用正確的因子
  6. **** ViewController上的Autolayout存在一些問題。 請清除現有約束,使用tableView填充視圖,然后從頂部 (非頂部布局指南 )將其設置為0px。

沒有更多的談話,代碼談論自己(它也有一些評論)

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修復

在此輸入圖像描述

輸出動畫

在此輸入圖像描述

希望它能滿足你的要求:)

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

在我看來, UICollectionviewUICollectionViewLayout更適合這類事情。 UITableView不會給你每個滾動變換,你需要做各種臟黑客。

如果你使用UICollectionview ,你要找的是-[UICollectionViewLayout layoutAttributesForElementsInRect:]-[UICollectionViewLayout shouldInvalidateLayoutForBoundsChange:] ,其他一切都是一些數學。

試試這個:你可以通過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)
//        })


}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM