How do you determine spacing between cells in UICollectionView flowLayout

后端 未结 12 1170
夕颜
夕颜 2020-11-29 15:32

I have a UICollectionView with a flow layout and each cell is a square. How do I determine the spacing between each cells on each row? I can\'t seem to find the appropriate

相关标签:
12条回答
  • 2020-11-29 15:56

    There are a few things to consider:

    1. Try changing the minimum spacing in IB, but leave the cursor in that field. Notice that Xcode doesn't immediately mark the document as changed. When you click in a different field, though, Xcode does notice that the document is changed and marks it so in the file navigator. So, be sure to tab or click over to a different field after making a change.

    2. Save your storyboard/xib file after making a change, and be sure to rebuild the app. It's not hard to miss that step, and then you're left scratching your head wondering why your changes didn't seem to have any effect.

    3. UICollectionViewFlowLayout has a minimumInteritemSpacing property, which is what you're setting in IB. But the collection's delegate can also have a method to determine the inter-item spacing. That method trump's the layout's property, so if you implement it in your delegate your layout's property won't be used.

    4. Remember that the spacing there is a minimum spacing. The layout will use that number (whether it comes from the property or from the delegate method) as the smallest allowable space, but it may use a larger space if it has space leftover on the line. So if, for example, you set the minimum spacing to 0, you may still see a few pixels between items. If you want more control over exactly how the items are spaced you should probably use a different layout (possibly one of your own creation).

    0 讨论(0)
  • 2020-11-29 15:59

    You can do it in two ways.

    Firstly, do some modification in layoutAttributesForItem,

    get the current attributes's layout via the previous layoutAttributesForItem(at: IndexPath(item: indexPath.item - 1, section: indexPath.section))?.frame.

    layoutAttributesForItem(at:): This method provides on demand layout information to the collection view. You need to override it and return the layout attributes for the item at the requested indexPath.

    Secondly, new some attributes via UICollectionViewLayoutAttributes(forCellWith: indexPath),layout them wherever you want.

    And the math is a little larger, because it performs the heavy lifting of the layout.

    layoutAttributesForElements(in:): In this method you need to return the layout attributes for all the items inside the given rectangle. You return the attributes to the collection view as an array of UICollectionViewLayoutAttributes.


    You can check My repo

    • you can put items left aligned

    • you can put items right aligned

    • you can put items right aligned and items reversed

    0 讨论(0)
  • 2020-11-29 15:59

    a swift version base on mokagio:https://github.com/fanpyi/UICollectionViewLeftAlignedLayout-Swift

    class UICollectionViewLeftAlignedLayout: UICollectionViewFlowLayout {
        override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            let attributesToReturn = super.layoutAttributesForElementsInRect(rect)
            if let attributesToReturn = attributesToReturn {
                for attributes in attributesToReturn {
                    if attributes.representedElementKind == nil {
                        let indexpath = attributes.indexPath
                        if let attr = layoutAttributesForItemAtIndexPath(indexpath) {
                            attributes.frame = attr.frame
                        }
    
                    }
                }
            }
            return attributesToReturn
        }
    
        override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
            if  let currentItemAttributes = super.layoutAttributesForItemAtIndexPath(indexPath){
                let sectionInset = self.evaluatedSectionInsetForItemAtIndex(indexPath.section)
                let  isFirstItemInSection = indexPath.item == 0;
                let  layoutWidth = CGRectGetWidth(self.collectionView!.frame) - sectionInset.left - sectionInset.right;
    
                if (isFirstItemInSection) {
                    currentItemAttributes.leftAlignFrameWithSectionInset(sectionInset)
                    return currentItemAttributes
                }
    
                let  previousIndexPath = NSIndexPath(forItem: indexPath.item - 1, inSection: indexPath.section)
    
                let previousFrame = layoutAttributesForItemAtIndexPath(previousIndexPath)?.frame ?? CGRectZero
                let  previousFrameRightPoint = previousFrame.origin.x + previousFrame.width
                let currentFrame = currentItemAttributes.frame;
                let  strecthedCurrentFrame = CGRectMake(sectionInset.left,
                    currentFrame.origin.y,
                    layoutWidth,
                    currentFrame.size.height)
                // if the current frame, once left aligned to the left and stretched to the full collection view
                // widht intersects the previous frame then they are on the same line
                let  isFirstItemInRow = !CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)
    
                if (isFirstItemInRow) {
                    // make sure the first item on a line is left aligned
                    currentItemAttributes.leftAlignFrameWithSectionInset(sectionInset)
                    return currentItemAttributes
                }
    
                var frame = currentItemAttributes.frame;
                frame.origin.x = previousFrameRightPoint + evaluatedMinimumInteritemSpacingForSectionAtIndex(indexPath.section)
                currentItemAttributes.frame = frame;
                return currentItemAttributes;
    
            }
            return nil
        }
        func evaluatedMinimumInteritemSpacingForSectionAtIndex(sectionIndex:Int) -> CGFloat {
            if let delegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout {
                if delegate.respondsToSelector("collectionView:layout:minimumInteritemSpacingForSectionAtIndex:") {
                    return delegate.collectionView!(self.collectionView!, layout: self, minimumInteritemSpacingForSectionAtIndex: sectionIndex)
    
                }
            }
            return self.minimumInteritemSpacing
    
        }
        func evaluatedSectionInsetForItemAtIndex(index: Int) ->UIEdgeInsets {
            if let delegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout {
                if  delegate.respondsToSelector("collectionView:layout:insetForSectionAtIndex:") {
                    return delegate.collectionView!(self.collectionView!, layout: self, insetForSectionAtIndex: index)
                }
            }
            return self.sectionInset
        }
    }
    
    0 讨论(0)
  • 2020-11-29 16:00

    An easy way to left-justify is to modify layoutAttributesForElementsInRect: in your subclass of UICollectionViewFlowLayout:

    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
        NSArray *allLayoutAttributes = [super layoutAttributesForElementsInRect:rect];
        CGRect prevFrame = CGRectMake(-FLT_MAX, -FLT_MAX, 0, 0);
        for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes)
        {
            //fix blur
            CGRect theFrame = CGRectIntegral(layoutAttributes.frame);
    
            //left justify
            if(prevFrame.origin.x > -FLT_MAX &&
               prevFrame.origin.y >= theFrame.origin.y &&
               prevFrame.origin.y <= theFrame.origin.y) //workaround for float == warning
            {
                theFrame.origin.x = prevFrame.origin.x +
                                    prevFrame.size.width + 
                                    EXACT_SPACE_BETWEEN_ITEMS;
            }
            prevFrame = theFrame;
    
            layoutAttributes.frame = theFrame;
        }
        return allLayoutAttributes;
    }
    
    0 讨论(0)
  • 2020-11-29 16:00

    A cleaner swift version for people interested, based on Chris Wagner's answer:

    class AlignLeftFlowLayout: UICollectionViewFlowLayout {
    
        var maximumCellSpacing = CGFloat(9.0)
    
        override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
            let attributesToReturn = super.layoutAttributesForElementsInRect(rect) as? [UICollectionViewLayoutAttributes]
    
            for attributes in attributesToReturn ?? [] {
                if attributes.representedElementKind == nil {
                    attributes.frame = self.layoutAttributesForItemAtIndexPath(attributes.indexPath).frame
                }
            }
    
            return attributesToReturn
        }
    
        override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
            let curAttributes = super.layoutAttributesForItemAtIndexPath(indexPath)
            let sectionInset = (self.collectionView?.collectionViewLayout as UICollectionViewFlowLayout).sectionInset
    
            if indexPath.item == 0 {
                let f = curAttributes.frame
                curAttributes.frame = CGRectMake(sectionInset.left, f.origin.y, f.size.width, f.size.height)
                return curAttributes
            }
    
            let prevIndexPath = NSIndexPath(forItem: indexPath.item-1, inSection: indexPath.section)
            let prevFrame = self.layoutAttributesForItemAtIndexPath(prevIndexPath).frame
            let prevFrameRightPoint = prevFrame.origin.x + prevFrame.size.width + maximumCellSpacing
    
            let curFrame = curAttributes.frame
            let stretchedCurFrame = CGRectMake(0, curFrame.origin.y, self.collectionView!.frame.size.width, curFrame.size.height)
    
            if CGRectIntersectsRect(prevFrame, stretchedCurFrame) {
                curAttributes.frame = CGRectMake(prevFrameRightPoint, curFrame.origin.y, curFrame.size.width, curFrame.size.height)
            } else {
                curAttributes.frame = CGRectMake(sectionInset.left, curFrame.origin.y, curFrame.size.width, curFrame.size.height)
            }
    
            return curAttributes
        }
    }
    
    0 讨论(0)
  • 2020-11-29 16:03

    The swift version of Chris solution.

    class PazLeftAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout {
        var maxCellSpacing = 14.0
        override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
            if var attributesToReturn = super.layoutAttributesForElementsInRect(rect) as? Array<UICollectionViewLayoutAttributes> {
                for attributes in attributesToReturn {
                    if attributes.representedElementKind == nil {
                        let indexPath = attributes.indexPath
                        attributes.frame = self.layoutAttributesForItemAtIndexPath(indexPath).frame;
                    }
                }
                return attributesToReturn;
            }
            return super.layoutAttributesForElementsInRect(rect)
        }
    
        override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
            let currentItemAttributes = super.layoutAttributesForItemAtIndexPath(indexPath)
    
            if let collectionViewFlowLayout = self.collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
                let sectionInset = collectionViewFlowLayout.sectionInset
                if (indexPath.item == 0) { // first item of section
                    var frame = currentItemAttributes.frame;
                    frame.origin.x = sectionInset.left; // first item of the section should always be left aligned
                    currentItemAttributes.frame = frame;
    
                    return currentItemAttributes;
                }
    
                let previousIndexPath = NSIndexPath(forItem:indexPath.item-1, inSection:indexPath.section)
                let previousFrame = self.layoutAttributesForItemAtIndexPath(previousIndexPath).frame;
                let previousFrameRightPoint = Double(previousFrame.origin.x) + Double(previousFrame.size.width) + self.maxCellSpacing
    
                let currentFrame = currentItemAttributes.frame
                var width : CGFloat = 0.0
                if let collectionViewWidth = self.collectionView?.frame.size.width {
                    width = collectionViewWidth
                }
                let strecthedCurrentFrame = CGRectMake(0,
                    currentFrame.origin.y,
                    width,
                    currentFrame.size.height);
    
                if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) { // if current item is the first item on the line
                    // the approach here is to take the current frame, left align it to the edge of the view
                    // then stretch it the width of the collection view, if it intersects with the previous frame then that means it
                    // is on the same line, otherwise it is on it's own new line
                    var frame = currentItemAttributes.frame;
                    frame.origin.x = sectionInset.left; // first item on the line should always be left aligned
                    currentItemAttributes.frame = frame;
                    return currentItemAttributes;
                }
    
                var frame = currentItemAttributes.frame;
                frame.origin.x = CGFloat(previousFrameRightPoint)
                currentItemAttributes.frame = frame;
            }
            return currentItemAttributes;
        }
    }
    

    To use it do the following:

        override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView.collectionViewLayout = self.layout
    }
    var layout : PazLeftAlignedCollectionViewFlowLayout {
        var layout = PazLeftAlignedCollectionViewFlowLayout()
        layout.itemSize = CGSizeMake(220.0, 230.0)
        layout.minimumLineSpacing = 12.0
        return layout
    }
    
    0 讨论(0)
提交回复
热议问题