UICollectionView自定义布局在dequeueReusableCell

时间:2016-07-12 20:03:58

标签: ios swift uicollectionview cell uicollectionviewlayout

正如我在上面的标题中所说,当我调用dequeueReusableCell方法来创建collectionView的单元格时,我的应用程序崩溃了。当我注意到如果我改变numberOfItemsInSection返回参数时,我很惊讶应用程序不想崩溃! WTF!如果numberOfItemsInSection返回介于1和4之间的值,则应用程序可以正常工作,相反,如果numberOfItemsInSection返回大于5的值,则app将无效。 我正在使用自定义布局(UltraVisulaLayout),显然dequeueReusableCell ReuseIdentifier参数与故事板相同。 这是我的班级代码:

import UIKit

class CoriViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int
    {
        return 1
    }

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)

        switch (indexPath.item! % 5)
        {
            case 0: cell.backgroundColor = UIColor.yellow()
            case 1: cell.backgroundColor = UIColor.orange()
            case 2: cell.backgroundColor = UIColor.red()
            case 3: cell.backgroundColor = UIColor.green()
            case 4: cell.backgroundColor = UIColor.blue()
            default:
            break
        }

        return cell
    }
}

这里是UltraVisualLayout.swift代码:

import UIKit

/* The heights are declared as constants outside of the class so they can be easily referenced elsewhere */
struct UltravisualLayoutConstants
{
    struct Cell
    {
        /* The height of the non-featured cell */
        static let standardHeight: CGFloat = 100
        /* The height of the first visible cell */
        static let featuredHeight: CGFloat = 280
    }
}

class UltravisualLayout: UICollectionViewLayout
{

    // MARK: Properties and Variables

    /* The amount the user needs to scroll before the featured cell changes */
    let dragOffset: CGFloat = 180.0

    var cache = [UICollectionViewLayoutAttributes]()

    /* Returns the item index of the currently featured cell */
    var featuredItemIndex: Int
    {
        get
        {
            /* Use max to make sure the featureItemIndex is never < 0 */
            return max(0, Int(collectionView!.contentOffset.y / dragOffset))
        }
    }

    /* Returns a value between 0 and 1 that represents how close the next cell is to becoming the featured cell */
    var nextItemPercentageOffset: CGFloat
    {
        get
        {
            return (collectionView!.contentOffset.y / dragOffset) - CGFloat(featuredItemIndex)
        }
    }

    /* Returns the width of the collection view */
    var width: CGFloat
    {
        get
        {
            return collectionView!.bounds.width
        }
    }

    /* Returns the height of the collection view */
    var height: CGFloat
    {
        get
        {
            return collectionView!.bounds.height
        }
    }

    /* Returns the number of items in the collection view */
    var numberOfItems: Int
    {
        get
        {
            return collectionView!.numberOfItems(inSection: 0)
        }
    }

    // MARK: UICollectionViewLayout

    /* Return the size of all the content in the collection view */
    override func collectionViewContentSize() -> CGSize
    {
        let contentHeight = (CGFloat(numberOfItems) * dragOffset) + (height - dragOffset)
        return CGSize(width: width, height: contentHeight)
    }

    override func prepare() {
        cache.removeAll(keepingCapacity: false)

        let standardHeight = UltravisualLayoutConstants.Cell.standardHeight
        let featuredHeight = UltravisualLayoutConstants.Cell.featuredHeight

        var frame = CGRect.zero
        var y: CGFloat = 0

        for item in 0..<numberOfItems
        {
            let indexPath = IndexPath(item: item, section: 0)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            /* Important because each cell has to slide over the top of the previous one */
            attributes.zIndex = item
            /* Initially set the height of the cell to the standard height */
            var height = standardHeight
            if (indexPath as NSIndexPath).item == featuredItemIndex
            {
                /* The featured cell */
                let yOffset = standardHeight * nextItemPercentageOffset
                y = collectionView!.contentOffset.y - yOffset
                height = featuredHeight
            } else if (indexPath as NSIndexPath).item == (featuredItemIndex + 1) && (indexPath as NSIndexPath).item != numberOfItems
            {
                /* The cell directly below the featured cell, which grows as the user scrolls */
                let maxY = y + standardHeight
                height = standardHeight + max((featuredHeight - standardHeight) * nextItemPercentageOffset, 0)
                y = maxY - height
            }
            frame = CGRect(x: 0, y: y, width: width, height: height)
            attributes.frame = frame
            cache.append(attributes)
            y = frame.maxY
        }
    }

    /* Return all attributes in the cache whose frame intersects with the rect passed to the method */
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
    {
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                layoutAttributes.append(attributes)
            }
        }
        return layoutAttributes
    }

    /* Return the content offset of the nearest cell which achieves the nice snapping effect, similar to a paged UIScrollView */
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint
    {
        let itemIndex = round(proposedContentOffset.y / dragOffset)
        let yOffset = itemIndex * dragOffset
        return CGPoint(x: 0, y: yOffset)
    }

    /* Return true so that the layout is continuously invalidated as the user scrolls */
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
    {
        return true
    }
}

这是我的最终目的: enter image description here

0 个答案:

没有答案