2

Hello i have working collectionview custom layout codes but some cells i need to change cell width in cell side comming varible from json dynamicly.

My CustomCollectionViewLayout

import UIKit

public var CELL_HEIGHT = 22.0
public var CELL_WIDTH = 40.0 // HERE WIDTH SOME CELLS MUST BE 80

class CustomCollectionViewLayout: UICollectionViewLayout {

    // Used for calculating each cells CGRect on screen.
    // CGRect will define the Origin and Size of the cell.

    let STATUS_BAR = UIApplication.sharedApplication().statusBarFrame.height

    // Dictionary to hold the UICollectionViewLayoutAttributes for
    // each cell. The layout attribtues will define the cell's size 
    // and position (x, y, and z index). I have found this process
    // to be one of the heavier parts of the layout. I recommend
    // holding onto this data after it has been calculated in either 
    // a dictionary or data store of some kind for a smooth performance.
    var cellAttrsDictionary = Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>()

    // Defines the size of the area the user can move around in
    // within the collection view.
    var contentSize = CGSize.zero

    // Used to determine if a data source update has occured.
    // Note: The data source would be responsible for updating
    // this value if an update was performed.
    var dataSourceDidUpdate = true

    override func collectionViewContentSize() -> CGSize {
        return self.contentSize
    }

    override func prepareLayout() {

        // Only update header cells.
        if !dataSourceDidUpdate {

            // Determine current content offsets.
            let xOffset = collectionView!.contentOffset.x
            let yOffset = collectionView!.contentOffset.y

            if collectionView?.numberOfSections() > 0 {
                for section in 0...collectionView!.numberOfSections()-1 {

                    // Confirm the section has items.
                    if collectionView?.numberOfItemsInSection(section) > 0 {

                        // Update all items in the first row.
                        if section == 0 {
                            for item in 0...collectionView!.numberOfItemsInSection(section)-1 {

                                // Build indexPath to get attributes from dictionary.
                                let indexPath = NSIndexPath(forItem: item, inSection: section)

                                // Update y-position to follow user.
                                if let attrs = cellAttrsDictionary[indexPath] {
                                    var frame = attrs.frame

                                    // Also update x-position for corner cell.
                                    if item == 0 {
                                        frame.origin.x = xOffset
                                    }

                                    frame.origin.y = yOffset
                                    attrs.frame = frame
                                }

                            }

                            // For all other sections, we only need to update
                            // the x-position for the fist item.
                        } else {

                            // Build indexPath to get attributes from dictionary.
                            let indexPath = NSIndexPath(forItem: 0, inSection: section)

                            // Update y-position to follow user.
                            if let attrs = cellAttrsDictionary[indexPath] {
                                var frame = attrs.frame
                                frame.origin.x = xOffset
                                attrs.frame = frame
                            }

                        }
                    }
                }
            }


            // Do not run attribute generation code
            // unless data source has been updated.
            return
        }

        // Acknowledge data source change, and disable for next time.
        dataSourceDidUpdate = false

        var maxItemInASection: Int?

        // Cycle through each section of the data source.
        if collectionView?.numberOfSections() > 0 {
            for section in 0...collectionView!.numberOfSections()-1 {

                // Cycle through each item in the section.
                if collectionView?.numberOfItemsInSection(section) > 0 {
                    for item in 0...collectionView!.numberOfItemsInSection(section)-1 {

                        // Build the UICollectionVieLayoutAttributes for the cell.
                        let cellIndex = NSIndexPath(forItem: item, inSection: section)
                        let xPos = Double(item) * CELL_WIDTH
                        let yPos = Double(section) * CELL_HEIGHT

                        let cellAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: cellIndex)
                        cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)

                        // Determine zIndex based on cell type.
                        if section == 0 && item == 0 {
                            cellAttributes.zIndex = 4
                        } else if section == 0 {
                            cellAttributes.zIndex = 3
                        } else if item == 0 {
                            cellAttributes.zIndex = 2
                        } else {
                            cellAttributes.zIndex = 1
                        }

                        // Save the attributes.
                        cellAttrsDictionary[cellIndex] = cellAttributes

                        if maxItemInASection < item {
                            maxItemInASection = item
                        }

                    }
                }

            }
        }

        // Update content size.
        let contentWidth = Double(maxItemInASection ?? 0) * CELL_WIDTH
        let contentHeight = Double(collectionView!.numberOfSections()) * CELL_HEIGHT
        self.contentSize = CGSize(width: contentWidth, height: contentHeight)

    }

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

        // Create an array to hold all elements found in our current view.
        var attributesInRect = [UICollectionViewLayoutAttributes]()

        // Check each element to see if it should be returned.
        for cellAttributes in cellAttrsDictionary.values.elements {
            if CGRectIntersectsRect(rect, cellAttributes.frame) {
                attributesInRect.append(cellAttributes)
            }
        }

        // Return list of elements.
        return attributesInRect
    }

    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        return cellAttrsDictionary[indexPath]!
    }

    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        return true
    }

}

And here my cell codes.

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("customCell", forIndexPath: indexPath) as! CustomCollectionViewCell

     if items[indexPath.section].base == 2 {

              cell.label.text = items[indexPath.section].base


             cell.frame.size.width = 80
             cell.layer.frame.size.width = 80

             CELL_WIDTH = 80.0 // HERE I CHANGED SOME CELLS WIDTH 80 BUT DONT CHANGE THEM !!

}else{

   cell.label.text = items[indexPath.section].base



}

You can see top side

CELL_WIDTH = 80.0 // HERE I CHANGED SOME CELLS WIDTH 80 BUT DONT CHANGE THEM !!

1
  • any idea ? i working but i didnt resolve this issue about 2 days. Commented Aug 16, 2016 at 9:18

3 Answers 3

3

You could implement collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) instead of subclassing UICollectionViewLayout - unless you have other reasons to do that.

It would go somewhat like:

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

    if indexPath.item % 3 == 0 { // <-- your condition here
        return CGSize(width: 40, height: 22) 
    }
    return CGSize(width: 80, height: 22)

}

By the way, don't forget to set your collectionView's delegate in order for this method to be called. 😁

UPDATED

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    if items[indexPath.section].base == 2 {
        return CGSize(width: 80, height: 22)
    } else {
        return CGSize(width: 44, height: 22)
    }
}

This is how I think your method should look like. At your cellForItem you only need to worry about setting the labels, let the above code take care of the size of the cells.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for answer but i added your code inside my viewcontroller but dont works , also items[indexPath.section].base line comming from json when json value = 2 will be do width * 2 that cell.label width
i need to use width * 2 codes inside -- > func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { } i think with if json value ...
What I was suggesting is for you to use the collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) method instead of implementing the UICollectionViewLayout subclass. Just adding the code as it is in the answer won't work. You would have to substitute your condition and appropriate size inside the method.
I updated the answer with what I think your code would look like. Tell me if it works =D
2
+50

sizeForItemAtIndexPath is not present for UICollectionViewLayout. So your CustomCollectionViewLayout inherited from UICollectionViewLayout does not have a protocol. You can create a protocol widthForItemAtIndexPath method.

protocol CustomCollectionViewDelegateLayout: NSObjectProtocol {
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, widthForItemAtIndexPath indexPath: NSIndexPath) -> CGFloat
}

In your viewController implemented this protocol method. Return value will be the width for the given indexPath

extension CustomCollectionViewController: CustomCollectionViewDelegateLayout {

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, widthForItemAtIndexPath indexPath: NSIndexPath) -> CGFloat {

        //from indexPath.section and indexPath.item identify double width item
        if sirano == 2 && satimdurum == 0 {
            return regularWidth * 2
        }
        return regularWidth
    }
}

In prepareLayout() method of your CustomCollectionViewLayout you can calculate position of each item you need to check which item should be double.

guard let width = delegate?.collectionView(collectionView!, layout: self, widthForItemAtIndexPath: cellIndex) else {
                        print("Please conform to CustomCollectionViewDelegateLayout protocol")
                        return
                    }

Made a demo project with Xcode 7.2 here at https://github.com/rishi420/UICollectionviewsample

Comments

1

Do not change cell size inside the cellForItemAtIndexPath method. The cell layout is already set after dequeueReusableCellWithReuseIdentifier.

Change it in the sizeForItemAtIndexPath. This is called before the above delegate method.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.