Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a Paging UICollectionView with Swift

I'm trying to create a UICollectionView with paging and that each item max width is 250 points, I've managed to create it, but I have 2 problems: The first item start not as it should be, but with more space at start and when I try to swipe, there is always something that wont let me swipe smooth.

This is how it looks:

video link

This is my code:

CenterCellCollectionFlowLayout.swift

class CenterCellCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {

        var attributesToReturn:[UICollectionViewLayoutAttributes] = super.layoutAttributesForElementsInRect(rect) as! [UICollectionViewLayoutAttributes]

        for var i = 0 ; i < attributesToReturn.count ; i++
        {
            var currentLayoutAttributes: UICollectionViewLayoutAttributes = attributesToReturn[i]
            var maximumSpacing: CGFloat = 50
            let origin: CGFloat
            if i - 1 >= 0 {
                let previousLayoutAttributes = attributesToReturn[i - 1]
                origin = previousLayoutAttributes.frame.maxX
            } else {
                origin = 0
            }

            if origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize().width
            {
                var frame: CGRect = currentLayoutAttributes.frame
                frame.origin.x = origin + maximumSpacing
                currentLayoutAttributes.frame = frame
            }
        }

        return attributesToReturn

    }

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

        if let cv = self.collectionView {

            let cvBounds = cv.bounds
            let halfWidth = cvBounds.size.width * 0.5;
            let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidth;

            if let attributesForVisibleCells = self.layoutAttributesForElementsInRect(cvBounds) as? [UICollectionViewLayoutAttributes] {

                var candidateAttributes : UICollectionViewLayoutAttributes?
                for attributes in attributesForVisibleCells {

                    // == Skip comparison with non-cell items (headers and footers) == //
                    if attributes.representedElementCategory != UICollectionElementCategory.Cell {
                        continue
                    }

                    if let candAttrs = candidateAttributes {

                        let a = attributes.center.x - proposedContentOffsetCenterX
                        let b = candAttrs.center.x - proposedContentOffsetCenterX

                        if fabsf(Float(a)) < fabsf(Float(b)) {
                            candidateAttributes = attributes;
                        }

                    }
                    else { // == First time in the loop == //

                        candidateAttributes = attributes;
                        continue;
                    }


                }

                return CGPoint(x : candidateAttributes!.center.x - halfWidth, y : proposedContentOffset.y);

            }

        }

        // Fallback
        return super.targetContentOffsetForProposedContentOffset(proposedContentOffset)
    }
}

MainViewController.swift

class MainViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var collectionViewFlowLayout: CenterCellCollectionViewFlowLayout!

    var collectionObjects: NSMutableArray?
    private let reuseIdentifier = "CollectionViewCell"

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
    {
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell
        cell.backgroundColor = UIColor.greenColor()
        // Configure the cell
        return cell
    }

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

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets
    {
        return UIEdgeInsetsMake(0, 0, 0, 0)
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
    {
        return CGSizeMake(250, 250)
    }
}

Thanks in advance

like image 319
ytpm Avatar asked Jul 11 '15 08:07

ytpm


People also ask

What is swift UICollectionView?

An object that manages an ordered collection of data items and presents them using customizable layouts.

How do I scroll horizontally in UICollectionView?

You need to reduce the height of UICollectionView to its cell / item height and select " Horizontal " from the " Scroll Direction " as seen in the screenshot below. Then it will scroll horizontally depending on the numberOfItems you have returned in its datasource implementation.

How does UICollectionView work?

UICollectionView makes adding custom layouts and layout transitions, like those in Photos, simple to build. You're not limited to stacks and grids because collection views are customizable. You can use them to make circle layouts, cover-flow style layouts, Pulse news style layouts and almost anything you can dream up!


2 Answers

A UICollectionView inherits from UIScrollView. It is possible to achieve paging by setting isPagingEnabled to true.

like image 60
chrisoneiota Avatar answered Oct 18 '22 01:10

chrisoneiota


So I figure out how to do it.

First create a custom UICollectionViewFlowLayout and add this override this method:

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

    if let cv = self.collectionView {

        let cvBounds = cv.bounds
        let halfWidth = cvBounds.size.width * 0.5;
        let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidth;

        if let attributesForVisibleCells = self.layoutAttributesForElementsInRect(cvBounds) as? [UICollectionViewLayoutAttributes] {

            var candidateAttributes : UICollectionViewLayoutAttributes?
            for attributes in attributesForVisibleCells {

                // == Skip comparison with non-cell items (headers and footers) == //
                if attributes.representedElementCategory != UICollectionElementCategory.Cell {
                    continue
                }

                if let candAttrs = candidateAttributes {

                    let a = attributes.center.x - proposedContentOffsetCenterX
                    let b = candAttrs.center.x - proposedContentOffsetCenterX

                    if fabsf(Float(a)) < fabsf(Float(b)) {
                        candidateAttributes = attributes;
                    }

                }
                else { // == First time in the loop == //

                    candidateAttributes = attributes;
                    continue;
                }


            }

            return CGPoint(x : candidateAttributes!.center.x - halfWidth, y : proposedContentOffset.y);

        }

    }

    // Fallback
    return super.targetContentOffsetForProposedContentOffset(proposedContentOffset)
}

Then on the class that you implement the UICollectionView do like that:

    let collectionViewLayout: CenterCellCollectionViewFlowLayout = CenterCellCollectionViewFlowLayout()
    collectionViewLayout.itemSize = CGSizeMake(self.itemSize, self.itemSize)
    collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    collectionViewLayout.minimumInteritemSpacing = 0
    collectionViewLayout.minimumLineSpacing = self.itemSpacing
    collectionViewLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal

    var collectionView: UICollectionView = UICollectionView(frame: self.collectionContainer.bounds, collectionViewLayout: collectionViewLayout)
    collectionView.delegate = self;
    collectionView.dataSource = self;
    collectionView.backgroundColor = UIColor.redColor()

    collectionView.registerClass(LevelsCustomCell.self, forCellWithReuseIdentifier: reuseIdentifier)
    collectionView.registerNib(UINib(nibName: reuseIdentifier, bundle: nil), forCellWithReuseIdentifier: reuseIdentifier)

    self.collectionContainer.addSubview(collectionView)

Thats about it, works like a charm.

like image 43
ytpm Avatar answered Oct 18 '22 01:10

ytpm