Norway


I have a custom for a . This custom increases the width of the center . Here is the custom class that does this. Look at the shiftedAttributes function to see how its done

class CustomCollectionViewLayout: UICollectionViewLayout {

    private var cache = [IndexPath: UICollectionViewLayoutAttributes]()
    private var contentWidth = CGFloat()
    private var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
    private var oldBounds = CGRect.zero
    private var cellWidth: CGFloat = 5
    private var collectionViewStartY: CGFloat {
        guard let collectionView = collectionView else {
            return 0
        }
        return collectionView.bounds.minY
    }
    private var collectionViewHeight: CGFloat {
        return collectionView!.frame.height
    }
    override public var collectionViewContentSize: CGSize {
        return CGSize(width: contentWidth, height: collectionViewHeight)
    }

    override public func prepare() {
        print(" prepare")
        guard let collectionView = collectionView,
            cache.isEmpty else {
                return
        }

        updateInsets()
        collectionView.decelerationRate = .fast
        cache.removeAll(keepingCapacity: true)
        cache = [IndexPath: UICollectionViewLayoutAttributes]()
        oldBounds = collectionView.bounds
        var xOffset: CGFloat = 0
        var cellWidth: CGFloat = 5

        for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
            let cellIndexPath = IndexPath(item: item, section: 0)
            let cellattributes = UICollectionViewLayoutAttributes(forCellWith: cellIndexPath)
            cellattributes.frame = CGRect(x: xOffset, y: 0, width: cellWidth, height: collectionViewHeight)
            xOffset = xOffset + cellWidth
            contentWidth = max(contentWidth,xOffset)
            cache[cellIndexPath] = cellattributes
        }
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        visibleLayoutAttributes.removeAll(keepingCapacity: true)

        for (_, attributes) in cache {
            visibleLayoutAttributes.append(self.shiftedAttributes(from: attributes))
        }
        return visibleLayoutAttributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        guard let attributes = cache[indexPath] else { fatalError("No attributes cached") }
        return shiftedAttributes(from: attributes)
    }
    override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        if oldBounds.size != newBounds.size {
            cache.removeAll(keepingCapacity: true)
        }
        return true
    }

    override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
        if context.invalidateDataSourceCounts { cache.removeAll(keepingCapacity: true) }
        super.invalidateLayout(with: context)
    }
}
extension CustomCollectionViewLayout {

    func updateInsets() {
        guard let collectionView = collectionView else { return }
        collectionView.contentInset.left = (collectionView.bounds.size.width - cellWidth) / 2
        collectionView.contentInset.right = (collectionView.bounds.size.width - cellWidth) / 2
    }
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset) }
        let midX: CGFloat = collectionView.bounds.size.width / 2
        guard let closestAttribute = findClosestAttributes(toXPosition: proposedContentOffset.x + midX) else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset) }
        return CGPoint(x: closestAttribute.center.x - midX, y: proposedContentOffset.y)
    }

    private func findClosestAttributes(toXPosition xPosition: CGFloat) -> UICollectionViewLayoutAttributes? {
        guard let collectionView = collectionView else { return nil }
        let searchRect = CGRect(
            x: xPosition - collectionView.bounds.width, y: collectionView.bounds.minY,
            width: collectionView.bounds.width * 2, height: collectionView.bounds.height
        )
        let closestAttributes = layoutAttributesForElements(in: searchRect)?.min(by: { abs($0.center.x - xPosition) < abs($1.center.x - xPosition) })
        return closestAttributes
    }
    private var continuousFocusedIndex: CGFloat {
        guard let collectionView = collectionView else { return 0 }
        let offset = collectionView.bounds.width / 2 + collectionView.contentOffset.x - cellWidth / 2
        return offset / cellWidth
    }
    private func shiftedAttributes(from attributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        guard let attributes = attributes.copy() as? UICollectionViewLayoutAttributes else { fatalError("Couldn't copy attributes") }
        let roundedFocusedIndex = round(continuousFocusedIndex)
        let focusedItemWidth = CGFloat(20)
        if attributes.indexPath.item == Int(roundedFocusedIndex){
            attributes.transform = CGAffineTransform(scaleX: , y: 1)
        } else {
            let translationDirection: CGFloat = attributes.indexPath.item < Int(roundedFocusedIndex) ? -1 : 1
            attributes.transform = CGAffineTransform(translationX: translationDirection * 20, y: 0)
        }
        return attributes
    }
}

Here is the View Controller which contains the collectionView that uses this layout:

class ViewController: UIViewController, UICollectionViewDelegate,UICollectionViewDataSource {

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = customCollectionView.dequeueReusableCell(withReuseIdentifier: "singleCell", for: indexPath)
        cell.backgroundColor = UIColor.random()
        return cell
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    @IBOutlet weak var picker: UIPickerView!
    @IBOutlet weak var customCollectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        customCollectionView.delegate = self
        customCollectionView.dataSource = self
        picker.delegate = self
        picker.dataSource = self
        // Do any additional setup after loading the view, typically from a nib.
    }

    @IBAction func goTo(_ sender: Any) {
        let indexPath = IndexPath(item: picker.selectedRow(inComponent: 0), section: 0)
        customCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    }

}

extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 10
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return String(row)
    }       
}

Note the pickerView where you can pick an index that is used in the goTo button to scrollTo the item at that index. Here it is in action:

enter image description here  - ARGLd - ios &#8211; How to use ScrollToItem(at:) when using a custom collectionView layout to alter cell sizes

See how even though I am staying on the same index, it keeps scrolling around, and doesn’t really scroll to that index anyway. When I don’t shift the attributes (with shiftedAttributes) and just return them normally in the custom layout, the scrollTo works fine.

So it seems something about the placement of each cell is used when doing scrollToItem(at:) which is getting confused by the shifted attributes? How do I scroll to a particular index when the sizes of the cells are subject to change?

EDIT: here is the entire project code if you wanna try it yourself:



Source link

LEAVE A REPLY

Please enter your comment!
Please enter your name here