How to center align the cells of a UICollectionView?

前端 未结 18 2464
梦如初夏
梦如初夏 2020-11-28 01:33

I am currently using UICollectionView for the user interface grid, and it works fine. However, I\'d like to be enable horizontal scrolling. The grid supports 8

相关标签:
18条回答
  • 2020-11-28 02:00

    Swift 2.0 Works fine for me!

     func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
            let edgeInsets = (screenWight - (CGFloat(elements.count) * 50) - (CGFloat(elements.count) * 10)) / 2
            return UIEdgeInsetsMake(0, edgeInsets, 0, 0);
        }
    

    Where: screenWight: basically its my collection's width (full screen width) - I made constants: let screenWight:CGFloat = UIScreen.mainScreen().bounds.width because self.view.bounds shows every-time 600 - coz of SizeClasses elements - array of cells 50 - my manual cell width 10 - my distance between cells

    0 讨论(0)
  • 2020-11-28 02:03

    My solution for static sized collection view cells which need to have padding on left and right-

    func collectionView(collectionView: UICollectionView, 
    layout collectionViewLayout: UICollectionViewLayout, 
    insetForSectionAtIndex section: Int) -> UIEdgeInsets {
    
        let flowLayout = (collectionViewLayout as! UICollectionViewFlowLayout)
        let cellSpacing = flowLayout.minimumInteritemSpacing
        let cellWidth = flowLayout.itemSize.width
        let cellCount = CGFloat(collectionView.numberOfItemsInSection(section))
    
        let collectionViewWidth = collectionView.bounds.size.width
    
        let totalCellWidth = cellCount * cellWidth
        let totalCellSpacing = cellSpacing * (cellCount - 1)
    
        let totalCellsWidth = totalCellWidth + totalCellSpacing
    
        let edgeInsets = (collectionViewWidth - totalCellsWidth) / 2.0
    
        return edgeInsets > 0 ? UIEdgeInsetsMake(0, edgeInsets, 0, edgeInsets) : UIEdgeInsetsMake(0, cellSpacing, 0, cellSpacing)
    }
    
    0 讨论(0)
  • 2020-11-28 02:04

    Similar to other answers, this is a dynamic answer that should work for static sized cells. The one modification I made is that I put the padding on both sides. If I didn't do this, I experienced problems.


    Objective-C

    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    
        NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
        CGFloat combinedItemWidth = (numberOfItems * collectionViewLayout.itemSize.width) + ((numberOfItems - 1) * collectionViewLayout.minimumInteritemSpacing);
        CGFloat padding = (collectionView.frame.size.width - combinedItemWidth) / 2;
    
        return UIEdgeInsetsMake(0, padding, 0, padding);
    }
    

    Swift

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
        let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: section))
        let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1)  * flowLayout.minimumInteritemSpacing)
        let padding = (collectionView.frame.width - combinedItemWidth) / 2
        return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
    }
    

    Also, if you are still are experiencing problems, make sure that you set both the minimumInteritemSpacing and minimumLineSpacing to the same values since these values seem to be interrelated.

    UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    flowLayout.minimumInteritemSpacing = 20.0;
    flowLayout.minimumLineSpacing = 20.0;
    
    0 讨论(0)
  • 2020-11-28 02:04

    This is how i obtained a collection view that was center aligned with a fixed Interitem spacing

    #define maxInteritemSpacing 6
    #define minLineSpacing 3
    
    @interface MyFlowLayout()<UICollectionViewDelegateFlowLayout>
    
    @end
    
    @implementation MyFlowLayout
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            self.minimumInteritemSpacing = 3;
            self.minimumLineSpacing = minLineSpacing;
            self.scrollDirection = UICollectionViewScrollDirectionVertical;
        }
        return self;
    }
    
    - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
        NSArray *answer = [super layoutAttributesForElementsInRect:rect];
    
        if (answer.count > 0) {
            NSMutableArray<NSMutableArray<UICollectionViewLayoutAttributes *> *> *rows = [NSMutableArray array];
            CGFloat maxY = -1.0f;
            NSInteger currentRow = 0;
    
            //add first item to row and set its maxY
            [rows addObject:[@[answer[0]] mutableCopy]];
            maxY = CGRectGetMaxY(((UICollectionViewLayoutAttributes *)answer[0]).frame);
    
            for(int i = 1; i < [answer count]; ++i) {
                //handle maximum spacing
                UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
                UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
                NSInteger maximumSpacing = maxInteritemSpacing;
                NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame);
    
                if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
                    CGRect frame = currentLayoutAttributes.frame;
                    frame.origin.x = origin + maximumSpacing;
                    currentLayoutAttributes.frame = frame;
                }
    
                //handle center align
                CGFloat currentY = currentLayoutAttributes.frame.origin.y;
                if (currentY >= maxY) {
                    [self shiftRowToCenterForCurrentRow:rows[currentRow]];
    
                    //next row
                    [rows addObject:[@[currentLayoutAttributes] mutableCopy]];
                    currentRow++;
                }
                else {
                    //same row
                    [rows[currentRow] addObject:currentLayoutAttributes];
                }
    
                maxY = MAX(CGRectGetMaxY(currentLayoutAttributes.frame), maxY);
            }
    
            //mark sure to shift 1 row items
            if (currentRow == 0) {
                [self shiftRowToCenterForCurrentRow:rows[currentRow]];
            }
        }
    
        return answer;
    }
    
    - (void)shiftRowToCenterForCurrentRow:(NSMutableArray<UICollectionViewLayoutAttributes *> *)currentRow
    {
        //shift row to center
        CGFloat endingX = CGRectGetMaxX(currentRow.lastObject.frame);
        CGFloat shiftX = (self.collectionViewContentSize.width - endingX) / 2.f;
        for (UICollectionViewLayoutAttributes *attr in currentRow) {
            CGRect shiftedFrame = attr.frame;
            shiftedFrame.origin.x += shiftX;
            attr.frame = shiftedFrame;
        }
    }
    
    @end
    
    0 讨论(0)
  • 2020-11-28 02:05

    Here is my solution with a few assumptions:

    • there is only one section
    • left and right insets are equal
    • cell height is the same

    Feel free to adjust to meet your needs.

    Centered layout with variable cell width:

    protocol HACenteredLayoutDelegate: UICollectionViewDataSource {
        func getCollectionView() -> UICollectionView
        func sizeOfCell(at index: IndexPath) -> CGSize
        func contentInsets() -> UIEdgeInsets
    }
    
    class HACenteredLayout: UICollectionViewFlowLayout {
        weak var delegate: HACenteredLayoutDelegate?
        private var cache = [UICollectionViewLayoutAttributes]()
        private var contentSize = CGSize.zero
        override var collectionViewContentSize: CGSize { return self.contentSize }
    
        required init(delegate: HACenteredLayoutDelegate) {
            self.delegate = delegate
            super.init()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        override func invalidateLayout() {
            cache.removeAll()
            super.invalidateLayout()
        }
    
        override func prepare() {
            if cache.isEmpty && self.delegate != nil && self.delegate!.collectionView(self.delegate!.getCollectionView(), numberOfItemsInSection: 0) > 0 {
                let insets = self.delegate?.contentInsets() ?? UIEdgeInsets.zero
                var rows: [(width: CGFloat, count: Int)] = [(0, 0)]
                let viewWidth: CGFloat = UIScreen.main.bounds.width
                var y = insets.top
                var unmodifiedIndexes = [IndexPath]()
                for itemNumber in 0 ..< self.delegate!.collectionView(self.delegate!.getCollectionView(), numberOfItemsInSection: 0) {
                    let indexPath = IndexPath(item: itemNumber, section: 0)
                    let cellSize = self.delegate!.sizeOfCell(at: indexPath)
                    let potentialRowWidth = rows.last!.width + (rows.last!.count > 0 ? self.minimumInteritemSpacing : 0) + cellSize.width + insets.right + insets.left
                    if potentialRowWidth > viewWidth {
                        let leftOverSpace = max((viewWidth - rows[rows.count - 1].width)/2, insets.left)
                        for i in unmodifiedIndexes {
                            self.cache[i.item].frame.origin.x += leftOverSpace
                        }
                        unmodifiedIndexes = []
                        rows.append((0, 0))
                        y += cellSize.height + self.minimumLineSpacing
                    }
                    unmodifiedIndexes.append(indexPath)
                    let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
                    rows[rows.count - 1].count += 1
                    rows[rows.count - 1].width += rows[rows.count - 1].count > 1 ? self.minimumInteritemSpacing : 0
                    attribute.frame = CGRect(x: rows[rows.count - 1].width, y: y, width: cellSize.width, height: cellSize.height)
                    rows[rows.count - 1].width += cellSize.width
                    cache.append(attribute)
                }
                let leftOverSpace = max((viewWidth - rows[rows.count - 1].width)/2, insets.left)
                for i in unmodifiedIndexes {
                    self.cache[i.item].frame.origin.x += leftOverSpace
                }
                self.contentSize = CGSize(width: viewWidth, height: y + self.delegate!.sizeOfCell(at: IndexPath(item: 0, section: 0)).height + insets.bottom)
            }
        }
    
        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            var layoutAttributes = [UICollectionViewLayoutAttributes]()
    
            for attributes in cache {
                if attributes.frame.intersects(rect) {
                    layoutAttributes.append(attributes)
                }
            }
            return layoutAttributes
        }
    
        override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            if indexPath.item < self.cache.count {
                return self.cache[indexPath.item]
            }
            return nil
        }
    }
    

    Result:

    0 讨论(0)
  • 2020-11-28 02:07

    It's easy to calculate insets dynamically, this code will always center your cells:

    NSInteger const SMEPGiPadViewControllerCellWidth = 332;
    
    ...
    
    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    {
    
        NSInteger numberOfCells = self.view.frame.size.width / SMEPGiPadViewControllerCellWidth;
        NSInteger edgeInsets = (self.view.frame.size.width - (numberOfCells * SMEPGiPadViewControllerCellWidth)) / (numberOfCells + 1);
    
        return UIEdgeInsetsMake(0, edgeInsets, 0, edgeInsets);
    }
    
    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
    {
        [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
        [self.collectionView.collectionViewLayout invalidateLayout];
    }
    
    0 讨论(0)
提交回复
热议问题