TMCoverFlowLayout

来源:互联网 发布:查车档案软件 编辑:程序博客网 时间:2024/06/06 00:46

A cover flow implementation by subclassing UICollectionViewFlowLayout

Original found on here, thanks to solomidSF(He must be a dota player:p), and I copy it using Swift with a little modifications

/***  Layout to add cover flow effect to collection view scrolling.*  Applicable only for horizontal flow direction.*/class TMCoverFlowLayout: UICollectionViewFlowLayout {    /**    *  Max degree that can be applied to individual item.    *  Default to 45 degrees.    */    var maxCoverDegree: CGFloat = 0    /**    *  Determines how elements covers each other.    *  Should be in range 0..1.    *  Default to 0.25.     *  Examples:    *  0 means that items are placed within a continious line.    *  0.5 means that half of 3 and 1 cover will be behind 2.    */    var coverDensity: CGFloat = 0    required init(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        self.commonInit()    }    override init() {        super.init()        self.commonInit()    }    override func prepareLayout() {        super.prepareLayout()        //This is a cover flow layout so forcely set these properties        self.scrollDirection = .Horizontal        self.collectionView?.pagingEnabled = true        assert(self.collectionView?.numberOfSections() == 1, "TMCoverFlowLayout: Multiple sections aren't supported!")        assert(self.scrollDirection == .Horizontal, "TMCoverFlowLayout: Vertical scrolling isn't supported!")    }    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {        return true    }    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {        var indexPaths = self.indexPathsContainedInRect(rect)        var resultingAttributes = [UICollectionViewLayoutAttributes]()        for path in indexPaths {            // We should create attributes by ourself.            var attributes = self.layoutAttributesForItemAtIndexPath(path)            resultingAttributes.append(attributes)        }        return resultingAttributes    }    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {        var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)        attributes.size = self.itemSize        attributes.center = CGPoint(x: self.collectionViewWidth() * CGFloat(indexPath.row) + self.collectionViewWidth(), y: self.collectionViewHeight()/2)        self.interpolateAttributes(attributes, forOffset: self.collectionView!.contentOffset.x)        return attributes    }    override func collectionViewContentSize() -> CGSize {        return CGSize(width: self.collectionView!.bounds.size.width * CGFloat(self.collectionView!.numberOfItemsInSection(0)), height: self.collectionView!.bounds.size.height)    }    func collectionViewHeight() -> CGFloat {        return self.collectionView!.bounds.size.height    }    func collectionViewWidth() -> CGFloat {        return self.collectionView!.bounds.size.width    }    func commonInit() {        self.maxCoverDegree = 15        self.coverDensity = 0    }    func itemCenterForRow(row: Int) -> CGPoint {        var collectionViewSize = self.collectionView!.bounds.size        return CGPoint(x: CGFloat(row) * collectionViewSize.width + collectionViewSize.width/2, y: collectionViewSize.height/2)    }    func minXForRow(row: Int) -> CGFloat {        return self.itemCenterForRow(row - 1).x + (1.0/2 - self.coverDensity) * self.itemSize.width    }    func maxXForRow(row: Int) -> CGFloat {        return self.itemCenterForRow(row + 1).x - (1.0/2 - self.coverDensity) * self.itemSize.width    }    func minXCenterForRow(row: Int) -> CGFloat {        var center = self.itemCenterForRow(row - 1).x        return center + (self.itemSize.width/2) * (1 - 2 * self.coverDensity + cos(self.degreesToRad(self.maxCoverDegree)))    }    func maxXCenterForRow(row: Int) -> CGFloat {        var center = self.itemCenterForRow(row + 1).x        return center - (self.itemSize.width/2) * (1 - 2 * self.coverDensity + cos(self.degreesToRad(self.maxCoverDegree)))    }    func degreesToRad(degrees: CGFloat) -> CGFloat {        return degrees * CGFloat(M_PI)/180.0    }    func indexPathsContainedInRect(rect: CGRect) -> [NSIndexPath] {        if self.collectionView!.numberOfItemsInSection(0) == 0 {            // Nothing to do here when we don't have items.            return [NSIndexPath]()        }        // Find min and max rows that can be determined for sure.        var minRow = Int(max(rect.origin.x/self.collectionViewWidth(), 0))        var maxRow = Int(CGRectGetMaxX(rect)/self.collectionViewWidth())        // Additional check for rows that also can be included (our rows are moving depending on content size).        var candidateMinRow = Int(max(minRow - 1, 0))        if self.maxXForRow(candidateMinRow) >= rect.origin.x {            // We have a row that is less than given minimum.            minRow = candidateMinRow        }        var candidateMaxRow = Int(min(maxRow + 1, self.collectionView!.numberOfItemsInSection(0) - 1))        if self.minXForRow(candidateMaxRow) <= CGRectGetMaxX(rect) {            maxRow = candidateMaxRow        }        // Simply add index paths between min and max.        var resultingIndexPaths = [NSIndexPath]()        for var i = minRow; i <= maxRow; i++ {            resultingIndexPaths.append(NSIndexPath(forRow: i, inSection: 0))        }        return resultingIndexPaths    }    func interpolateAttributes(attributes: UICollectionViewLayoutAttributes, forOffset offset: CGFloat) {        var attributesPath = attributes.indexPath        // Interpolate offset for given attribute. For this task we need min max interval and min and max x allowed for item.        var minInterval = CGFloat(attributesPath.row - 1) * self.collectionViewWidth()        var maxInterval = CGFloat(attributesPath.row + 1) * self.collectionViewWidth()        var minX = self.minXCenterForRow(attributesPath.row)        var maxX = self.maxXCenterForRow(attributesPath.row)        // Interpolate by formula        var interpolatedX = min(max(minX + ((maxX - minX)/(maxInterval - minInterval)) * (offset - minInterval), minX), maxX)        attributes.center = CGPoint(x: interpolatedX, y: attributes.center.y)        // Interpolate position into angle by formula.        var angle = -self.maxCoverDegree + (interpolatedX - minX) * 2 * self.maxCoverDegree / (maxX - minX)        var transform = CATransform3DIdentity        // Add perspective.        transform.m34 = -1/500.0        // Then rotate.//        transform = CATransform3DRotate(transform, angle * CGFloat(M_PI)/180, 0, 1, 0)        // Or scale        let scale = max(1 - abs(angle)/45, 0.9)        transform = CATransform3DScale(transform, scale, scale, scale)        let alpha = max(1 - abs(angle)/45, 0.5)        attributes.alpha = alpha        attributes.transform3D = transform        attributes.zIndex = NSIntegerMax - attributesPath.row    }}
0 0
原创粉丝点击