2

I have a CollectionView showing a number of images with horizontal scroll and paging enabled. The first and the last pages are showing perfectly but rest of the pages are not showing properly, they are showing parts of images from the adjacent page due to which they are not aligned properly (screenshots attached). How do I make all pages look same i.e. the images properly center aligned so that adjacent pages don't peek.

Notice the image peeking in Page 2 from the left side due to which the actual image on is getting clipped from the left. How can I fix this so that all pages look like page 1.

jtbandes
  • 115,675
  • 35
  • 233
  • 266
user3789185
  • 71
  • 1
  • 9
  • Try enabling paging in collectionview – iAhmed Sep 04 '15 at 06:10
  • I have already mentioned that paging is enabled. – user3789185 Sep 04 '15 at 06:12
  • As I can see there are only 3 cells which can fit in a row on screen, however even on the first screenshot last image is a little bit cropped. Is not it? – gontovnik Sep 04 '15 at 06:12
  • If you want to make paging, you should make sure that each your page is equal width. If you have collection view with width = 500 and you want to have 5 pages, you should have content size width equal to 2500, otherwise it will look weird and sometimes may crop pages. – gontovnik Sep 04 '15 at 06:17
  • It is not perfectly alright on your screenshot. I have checked in photoshop. Check it there: http://prntscr.com/8cemc4. You can see 10 pixels transparent on the right. That's how it should be. It means that your image is cropped. – gontovnik Sep 04 '15 at 06:19
  • I found a similar question which I hope is dealing with the same problem: [UICollectionView align logic missing in horizontal paging scrollview](http://stackoverflow.com/questions/13228600/uicollectionview-align-logic-missing-in-horizontal-paging-scrollview) but the solutions are in Objective-C. If anyone could provide the code in swift. – user3789185 Sep 04 '15 at 06:20
  • Check my new answer. Added swift solution. – gontovnik Sep 04 '15 at 06:36

1 Answers1

1

As you have replied in the comment, answer can be found here. Here is the Swift solution you have asked for.

Swift 1.2

override func collectionViewContentSize() -> CGSize {
    // Only support single section for now.
    // Only support Horizontal scroll
    let count = collectionView!.dataSource!.collectionView(collectionView!, numberOfItemsInSection: 0)

    let canvasSize = collectionView!.frame.size
    var contentSize = canvasSize

    if (scrollDirection == .Horizontal) {
        let rowCount = (canvasSize.height - itemSize.height) / (itemSize.height + minimumInteritemSpacing) + 1
        let columnCount = (canvasSize.width - itemSize.width) / (itemSize.width + minimumLineSpacing) + 1;
        let page = ceil(CGFloat(count) / (CGFloat)(rowCount * columnCount));
        contentSize.width = page * canvasSize.width;
    }

    return contentSize;
}

func frameForItemAtIndexPath(indexPath: NSIndexPath!) -> CGRect {
    let canvasSize = collectionView!.frame.size

    let rowCount = (canvasSize.height - itemSize.height) / (itemSize.height + minimumInteritemSpacing) + 1
    let columnCount = (canvasSize.width - itemSize.width) / (itemSize.width + minimumLineSpacing) + 1

    let pageMarginX = (canvasSize.width - columnCount * itemSize.width - (columnCount > 1 ? (columnCount - 1) * minimumLineSpacing : 0)) / 2.0
    let pageMarginY = (canvasSize.height - rowCount * itemSize.height - (rowCount > 1 ? (rowCount - 1) * minimumInteritemSpacing : 0)) / 2.0

    let page = CGFloat(indexPath.row) / (rowCount * columnCount)
    let remainder = CGFloat(indexPath.row) - page * (rowCount * columnCount)
    let row = remainder / columnCount
    let column = remainder - row * columnCount

    var cellFrame = CGRect.zeroRect
    cellFrame.origin.x = pageMarginX + column * (itemSize.width + minimumLineSpacing);
    cellFrame.origin.y = pageMarginY + row * (itemSize.height + minimumInteritemSpacing);
    cellFrame.size.width = itemSize.width;
    cellFrame.size.height = itemSize.height;

    if (scrollDirection == .Horizontal) {
        cellFrame.origin.x += page * canvasSize.width;
    }

    return cellFrame;
}

override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
    var attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
    attributes.frame = frameForItemAtIndexPath(indexPath)
    return attributes
}

override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
    let superAttrs = super.layoutAttributesForElementsInRect(rect)
    if let originAttrs = superAttrs as? [UICollectionViewLayoutAttributes] {
        var attrs = [UICollectionViewLayoutAttributes]()

        for (index, var attr) in enumerate(originAttrs) {
            let indexPath = attr.indexPath
            let itemFrame = frameForItemAtIndexPath(indexPath)
            if CGRectIntersectsRect(itemFrame, rect) {
                attr = layoutAttributesForItemAtIndexPath(indexPath)
                attrs.append(attr)
            }
        }

        return attrs
    }

    return superAttrs;
}

Swift 2.0

override func collectionViewContentSize() -> CGSize {
    // Only support single section for now.
    // Only support Horizontal scroll
    let count = collectionView!.dataSource!.collectionView(collectionView!, numberOfItemsInSection: 0)

    let canvasSize = collectionView!.frame.size
    var contentSize = canvasSize

    if (scrollDirection == .Horizontal) {
        let rowCount = (canvasSize.height - itemSize.height) / (itemSize.height + minimumInteritemSpacing) + 1
        let columnCount = (canvasSize.width - itemSize.width) / (itemSize.width + minimumLineSpacing) + 1;
        let page = ceil(CGFloat(count) / (CGFloat)(rowCount * columnCount));
        contentSize.width = page * canvasSize.width;
    }

    return contentSize;
}

func frameForItemAtIndexPath(indexPath: NSIndexPath!) -> CGRect {
    let canvasSize = collectionView!.frame.size

    let rowCount = (canvasSize.height - itemSize.height) / (itemSize.height + minimumInteritemSpacing) + 1
    let columnCount = (canvasSize.width - itemSize.width) / (itemSize.width + minimumLineSpacing) + 1

    let pageMarginX = (canvasSize.width - columnCount * itemSize.width - (columnCount > 1 ? (columnCount - 1) * minimumLineSpacing : 0)) / 2.0
    let pageMarginY = (canvasSize.height - rowCount * itemSize.height - (rowCount > 1 ? (rowCount - 1) * minimumInteritemSpacing : 0)) / 2.0

    let page = CGFloat(indexPath.row) / (rowCount * columnCount)
    let remainder = CGFloat(indexPath.row) - page * (rowCount * columnCount)
    let row = remainder / columnCount
    let column = remainder - row * columnCount

    var cellFrame = CGRect.zeroRect
    cellFrame.origin.x = pageMarginX + column * (itemSize.width + minimumLineSpacing);
    cellFrame.origin.y = pageMarginY + row * (itemSize.height + minimumInteritemSpacing);
    cellFrame.size.width = itemSize.width;
    cellFrame.size.height = itemSize.height;

    if (scrollDirection == .Horizontal) {
        cellFrame.origin.x += page * canvasSize.width;
    }

    return cellFrame;
}

override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
    attributes!.frame = frameForItemAtIndexPath(indexPath)
    return attributes
}

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    let superAttrs = super.layoutAttributesForElementsInRect(rect)
    if let originAttrs = superAttrs as [UICollectionViewLayoutAttributes]! {
        var attrs = [UICollectionViewLayoutAttributes]()

        for (_, var attr) in originAttrs.enumerate() {
            let indexPath = attr.indexPath
            let itemFrame = frameForItemAtIndexPath(indexPath)
            if CGRectIntersectsRect(itemFrame, rect) {
                attr = layoutAttributesForItemAtIndexPath(indexPath)!
                attrs.append(attr)
            }
        }

        return attrs
    }

    return superAttrs;
}
Community
  • 1
  • 1
gontovnik
  • 690
  • 5
  • 9
  • Are you using Swift 1.2 or Swift 2.0? It has difference. – gontovnik Sep 04 '15 at 06:40
  • I found a solution in swift which is working: [Horizontal paging view custom layout](http://pastebin.com/Rc9YRxWV), can you please guide how to add a spacing between cells, because all that is missing is the cell spacing. The cells are sticking to one another without any space. – user3789185 Sep 04 '15 at 06:43
  • Collection view flow layout has _var_ called **minimumInteritemSpacing**. It defines the minimum spacing to use between items in the same row. – gontovnik Sep 04 '15 at 06:45
  • I have updated my answer and added Swift 2.0 solution. – gontovnik Sep 04 '15 at 06:46
  • Could you please tell where do I specify the **minimumInteritemSpacing**. I am learning Swift and am not much familiar with collectionview – user3789185 Sep 04 '15 at 06:56
  • Are you going to use subclass of UICollectionViewFlowLayout? If yes, then in *init* method you can say `minimumInteritemSpacing = 10.0`. – gontovnik Sep 04 '15 at 06:57
  • If you are just started learning collection view with swift, then [this tutorial](http://www.raywenderlich.com/78550/beginning-ios-collection-views-swift-part-1) will be useful. – gontovnik Sep 04 '15 at 06:58
  • Please refer to my code [Horizontal paging view custom layout](http://pastebin.com/Rc9YRxWV). I added an init along with init(coder: ). Causes error. – user3789185 Sep 04 '15 at 07:00
  • *UICollectionViewLayout* does not have *minimumInteritemSpacing*, you should calculate it manually. – gontovnik Sep 04 '15 at 07:01
  • In you func *calculateCellFrameHorizontalPosition* you should add calculation for spacing. – gontovnik Sep 04 '15 at 07:04