CollectionView不显示单元格

时间:2015-11-21 13:51:15

标签: ios swift uikit ios9 uikit-dynamics

我试图将UIDynamics弹性添加到Swift中的水平UICollectionViewFlowLayout

我的视图控制器非常基本,只显示30个绿色的库存项目:

import Foundation
import UIKit

class MainViewController: UIViewController {

    private var collectionView: UICollectionView?
    private let cellIdentifier = "Cell"

    init() {
        super.init(nibName: nil, bundle: nil)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = SpringyCollectionViewLayout()

        let collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.backgroundColor = UIColor.whiteColor()
        collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: cellIdentifier)
        collectionView.decelerationRate = 0.9

        view.addSubview(collectionView)

        self.collectionView = collectionView

        view.backgroundColor = UIColor.whiteColor()
    }

    override func prefersStatusBarHidden() -> Bool {
        return true
    }
}

extension MainViewController : UICollectionViewDataSource {
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 30
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) 
        cell.clipsToBounds = true
        cell.backgroundColor = UIColor.greenColor()

        return cell
    }
}

extension MainViewController : UICollectionViewDelegate {
}

我的布局子类如下所示:

import Foundation
import UIKit

let kLength = 0.3
let kDamping = 5.5
let kFrequence = 1.3
let kResistence = 1000

class SpringyCollectionViewLayout: UICollectionViewFlowLayout {
    var dynamicAnimator: UIDynamicAnimator!
    var visibleIndexPathsSet: NSMutableSet!
    var latestDelta = CGFloat()

    override init() {
        super.init()

        sectionInset = UIEdgeInsetsMake(0, 20, 0, 20)

        itemSize = CGSizeMake(UIScreen.mainScreen().bounds.width * 0.5, UIScreen.mainScreen().bounds.height * 0.8)
        minimumLineSpacing = 20
        scrollDirection = .Horizontal

        self.dynamicAnimator = UIDynamicAnimator(collectionViewLayout: self)
        self.visibleIndexPathsSet = NSMutableSet()
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepareLayout() {
        super.prepareLayout()

        // Need to overflow our actual visible rect slightly to avoid flickering.
        let visibleRect = CGRectInset(self.collectionView!.bounds, -100, -100)
        let itemsInVisibleRectArray: NSArray = super.layoutAttributesForElementsInRect(visibleRect)!
        let itemsIndexPathsInVisibleRectSet: NSSet = NSSet(array: itemsInVisibleRectArray.valueForKey("indexPath") as! [AnyObject])

        // Step 1: Remove any behaviours that are no longer visible.
        let noLongerVisibleBehaviours = (self.dynamicAnimator.behaviors as NSArray).filteredArrayUsingPredicate(NSPredicate(block: {behaviour, bindings in
            let currentlyVisible: Bool = itemsIndexPathsInVisibleRectSet.member((behaviour as! UIAttachmentBehavior).items.first!
                ) != nil
            return !currentlyVisible
        }))

        for (_, obj) in noLongerVisibleBehaviours.enumerate() {
            self.dynamicAnimator.removeBehavior(obj as! UIDynamicBehavior)
            self.visibleIndexPathsSet.removeObject((obj as! UIAttachmentBehavior).items.first!)
        }

        // Step 2: Add any newly visible behaviours.
        // A "newly visible" item is one that is in the itemsInVisibleRect(Set|Array) but not in the visibleIndexPathsSet
        let newlyVisibleItems = itemsInVisibleRectArray.filteredArrayUsingPredicate(NSPredicate(block: {item, bindings in
            let currentlyVisible: Bool = self.visibleIndexPathsSet.member(item.indexPath) != nil

            return !currentlyVisible
        }))

        let touchLocation: CGPoint = self.collectionView!.panGestureRecognizer.locationInView(self.collectionView)

        for (_, item) in newlyVisibleItems.enumerate() {
            let springBehaviour: UIAttachmentBehavior = UIAttachmentBehavior(item: item as! UIDynamicItem, attachedToAnchor: item.center)

            springBehaviour.length = CGFloat(kLength)
            springBehaviour.damping = CGFloat(kDamping)
            springBehaviour.frequency = CGFloat(kFrequence)

            // If our touchLocation is not (0,0), we'll need to adjust our item's center "in flight"
            if (!CGPointEqualToPoint(CGPointZero, touchLocation)) {
                let yDistanceFromTouch = fabsf(Float(touchLocation.y - springBehaviour.anchorPoint.y))
                let xDistanceFromTouch = fabsf(Float(touchLocation.x - springBehaviour.anchorPoint.x))
                let scrollResistance = (yDistanceFromTouch + xDistanceFromTouch) / Float(kResistence)

                let item = springBehaviour.items.first as! UICollectionViewLayoutAttributes
                var center = item.center

                if self.latestDelta < 0 {
                    center.x += max(self.latestDelta, self.latestDelta * CGFloat(scrollResistance))
                } else {
                    center.x += min(self.latestDelta, self.latestDelta * CGFloat(scrollResistance))
                }

                item.center = center
            }

            self.dynamicAnimator.addBehavior(springBehaviour)
            self.visibleIndexPathsSet.addObject(item.indexPath)
        }
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return self.dynamicAnimator.itemsInRect(rect) as? [UICollectionViewLayoutAttributes]
    }

    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        return self.dynamicAnimator.layoutAttributesForCellAtIndexPath(indexPath)
    }

    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        let scrollView = self.collectionView!
        let delta: CGFloat = newBounds.origin.x - scrollView.bounds.origin.x

        self.latestDelta = delta

        let touchLocation: CGPoint = self.collectionView!.panGestureRecognizer.locationInView(self.collectionView)

        for (_, springBehaviour) in self.dynamicAnimator.behaviors.enumerate() {
            let springBehav = (springBehaviour as! UIAttachmentBehavior)
            let yDistanceFromTouch = fabsf(Float(touchLocation.y - springBehav.anchorPoint.y))
            let xDistanceFromTouch = fabsf(Float(touchLocation.x - springBehav.anchorPoint.x))
            let scrollResistance = (yDistanceFromTouch + xDistanceFromTouch) / Float(kResistence)

            let item = springBehav.items.first as! UICollectionViewLayoutAttributes
            var center = item.center

            if self.latestDelta < 0 {
                center.x += max(self.latestDelta, self.latestDelta * CGFloat(scrollResistance))
            } else {
                center.x += min(self.latestDelta, self.latestDelta * CGFloat(scrollResistance))
            }

            item.center = center;

            self.dynamicAnimator.updateItemUsingCurrentState(item)
        }

        return false
    }

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

        var offsetAdjustment = CGFloat(MAXFLOAT)
        let center = proposedContentOffset.x + (CGRectGetWidth(self.collectionView!.bounds) / 2.0)
        let proposedRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView!.bounds.size.width, self.collectionView!.bounds.size.height)

        let array:NSArray = self.layoutAttributesForElementsInRect(proposedRect)!

        for layoutAttributes : AnyObject in array {

            if let _layoutAttributes = layoutAttributes as? UICollectionViewLayoutAttributes {

                if _layoutAttributes.representedElementCategory != UICollectionElementCategory.Cell {
                    continue
                }

                let itemVerticalCenter:CGFloat = layoutAttributes.center.x

                let _center = fabsf(Float(itemVerticalCenter) - Float(center))
                let _offsetAdjustment = fabsf(Float(offsetAdjustment))

                if (_center < _offsetAdjustment) {
                    offsetAdjustment = (itemVerticalCenter - center)
                }
            }
        }

        return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y)
    }
}

集合视图使用正确的contentSize呈现,但单元格不存在:

empty cells

如果我从layoutAttributesForElementsInRect删除了返回itemsInRect结果的UIDynamicAnimator覆盖,则会显示单元格 - 尽管没有动态行为:

visible cells

知道我错过了什么?我之前在Objective-C和iOS 8中做过同样的事情 - 当我比较它看起来相似的代码时。

我调试了prepareLayout,它确实在可见的矩形中找到了单元格,并且行为确实被添加到动画师中。

0 个答案:

没有答案