I have a UICollectionView
with random cells. Is there any method that allows me to center rows?
This is how it looks by default:
[ x x x x
I had to do something like this, but needed all the cells in the one section. It was pretty simple to extend UICollectionViewFlowLayout
to center cells. I made a pod:
https://github.com/keighl/KTCenterFlowLayout
Just link this flow layout. You can align center, left, right also.
//
// CellAllignmentFlowLayout.swift
// UICollectionView
//
// Created by rajeshkumar Lingavel on 8/11/15.
// Copyright © 2015 rajeshkumar Lingavel. All rights reserved.
//
import UIKit
enum SZAlignment:Int {
case Center,
left,
Right
}
class CellAllignmentFlowLayout: UICollectionViewFlowLayout {
var alignment:SZAlignment!
var padding:CGFloat!
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// NSArray *allAttributesInRect = [super
// layoutAttributesForElementsInRect:rect];
let allAttributesInRect:NSArray = super.layoutAttributesForElementsInRect(rect)!
var changedAttributes:NSArray = NSArray()
switch(alignment.rawValue){
case 0:
changedAttributes = alignCenter(allAttributesInRect)
case 1:
changedAttributes = alignLeft(allAttributesInRect)
case 2:
changedAttributes = alignRight(allAttributesInRect)
default:
assertionFailure("No Direction")
}
return changedAttributes as? [UICollectionViewLayoutAttributes]
}
private func alignCenter(allAttributesInRect:NSArray) -> NSArray{
let numberOfSection:Int = (self.collectionView?.numberOfSections())!
let redefiendArray = NSMutableArray()
for i in 0 ..< numberOfSection {
let thisSectionObjects = sectionObjects(allAttributesInRect, section: i)
let totalLines = numberOfLines(thisSectionObjects, section: i)
let lastrowObjects = lastRow(thisSectionObjects, numberOfRows: totalLines, section: i)
let lastRowObjectsRow = setMiddleTheLastRow(lastrowObjects)
let start = (thisSectionObjects.count - lastrowObjects.count)
for j in start..<thisSectionObjects.count{
thisSectionObjects.replaceObjectAtIndex(j, withObject: lastRowObjectsRow.objectAtIndex(j - start))
}
redefiendArray.addObjectsFromArray(thisSectionObjects as [AnyObject])
}
return redefiendArray
}
private func alignLeft(allAttributesInRect:NSArray) -> NSArray{
return allAttributesInRect;
}
private func alignRight(allAttributesInRect:NSArray) -> NSArray{
return allAttributesInRect;
}
private func getTotalLenthOftheSection(section:Int,allAttributesInRect:NSArray) -> CGFloat{
var totalLength:CGFloat = 0.0
totalLength = totalLength + (CGFloat (((self.collectionView?.numberOfItemsInSection(section))! - 1)) * padding)
for attributes in allAttributesInRect {
if(attributes.indexPath.section == section){
totalLength = totalLength + attributes.frame.width
}
}
return totalLength
}
private func numberOfLines(allAttributesInRect:NSArray,section:Int)-> Int{
var totalLines:Int = 0
for attributes in allAttributesInRect {
if(attributes.indexPath.section == section){
if (attributes.frame.origin.x == self.sectionInset.left){
totalLines = totalLines + 1
}
}
}
return totalLines
}
private func sectionObjects(allAttributesInRect:NSArray,section:Int) -> NSMutableArray{
let objects:NSMutableArray = NSMutableArray()
for attributes in allAttributesInRect {
if(attributes.indexPath.section == section){
objects.addObject(attributes)
}
}
return objects
}
private func lastRow(allAttributesInRect:NSArray,numberOfRows:Int,section:Int) -> NSMutableArray{
var totalLines:Int = 0
let lastRowArrays:NSMutableArray = NSMutableArray()
for attributes in allAttributesInRect {
if(attributes.indexPath.section == section){
if (attributes.frame.origin.x == self.sectionInset.left){
totalLines = totalLines + 1
if(totalLines == numberOfRows){
lastRowArrays.addObject(attributes)
}
}
else{
if(totalLines == numberOfRows){
lastRowArrays.addObject(attributes)
}
}
}
}
return lastRowArrays
}
private func setMiddleTheLastRow(lastRowAttrs:NSMutableArray)->NSMutableArray{
let redefinedValues = NSMutableArray()
let totalLengthOftheView = self.collectionView?.frame.width
var totalLenthOftheCells:CGFloat = 0.0
totalLenthOftheCells = totalLenthOftheCells + (CGFloat (lastRowAttrs.count) - 1) * padding
for attrs in lastRowAttrs{
totalLenthOftheCells = totalLenthOftheCells + attrs.frame.width
}
var initalValue = (totalLengthOftheView!/2) - (totalLenthOftheCells/2)
for i in 0..<lastRowAttrs.count {
let changeingAttribute:UICollectionViewLayoutAttributes = lastRowAttrs[i] as! UICollectionViewLayoutAttributes
var frame = changeingAttribute.frame
frame.origin.x = initalValue
changeingAttribute.frame = frame
redefinedValues.addObject(changeingAttribute)
initalValue = initalValue + changeingAttribute.frame.width + padding
}
return redefinedValues;
}
}
A bit of background first - a UICollectionView
is combined with a UICollectionViewLayout
which determines how the cells get placed in the view. This means a collection view is very flexible (you can create almost any layout with it), but also means modifying layouts can be a little confusing.
Creating an entirely new layout class is complex, so instead you want to try and modify the default layout (UICollectionViewFlowLayout
) to get your center alignment. To make it even simpler, you probably want to avoid subclassing the flow layout itself.
Here's one approach (it may not be the best approach, but it's the first one I can think of) - split your cells into two sections, as follows:
[ x x x x x ] <-- Section 1
[ x x x x x ] <-- Section 1
[ x x ] <-- Section 2
This should be fairly straightforward, provided you know the width of your scroll view and the number of cells that can fit in each row.
Then, use the collectionView:layout:insetForSectionAtIndex:
delegate method to set the margins for your second section so as it appears to be vertically centered. Once you've done this, you just need to ensure you recompute the appropriate section divisions/insets so both portrait and landscape orientations can be supported.
There is a somewhat similar question here - How to center align the cells of a UICollectionView? - that goes into more details about the inset methods, although it's not quite trying to do the same thing that you are.