UICollectionView의 셀을 가운데 정렬하는 방법은 무엇입니까?


99

현재 UICollectionView사용자 인터페이스 그리드를 사용하고 있으며 제대로 작동합니다. 그러나 가로 스크롤을 사용하고 싶습니다. 그리드는 페이지 당 8 개 항목을 지원하며 총 항목 수가 4 개일 때 가로 스크롤 방향이 활성화 된 상태에서 항목을 정렬하는 방법은 다음과 같습니다.

0 0 x x
0 0 x x

여기에 0-> 컬렉션 항목 및 x-> 빈 셀

다음과 같이 가운데 정렬하는 방법이 있습니까?

x 0 0 x
x 0 0 x

콘텐츠가 더 깔끔하게 보이도록?

또한 아래 배열이 내가 기대하는 해결책이 될 수도 있습니다.

0 0 0 0
x x x x

답변:


80

다음과 같이 구현하면 한 줄 모양을 얻을 수 있다고 생각합니다.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 100, 0, 0);
}

콘텐츠를 한 줄로 만드는 방법을 알아 내려면 해당 번호를 가지고 놀아야합니다. 첫 번째 0은 상단 가장자리 인수입니다. 화면에서 콘텐츠를 세로로 중앙에 배치하려면이 인수도 조정할 수 있습니다.


1
이것은 좋은 해결책이긴하지만 제 측에서 추가 계산이 필요합니다.
RVN

이 값을 어떻게 계산합니까?
jjxtra

7
삽입을 동적으로 계산하려면 : CGFloat leftInset = (collectionView.frame.size.width-40 * [collectionView numberOfItemsInSection : section]) / 2; 40 = 세포 크기
Abduliam Rehmanius 2013 년

1
@AbduliamRehmanius 그러나 이것은 셀이 수평으로 간격이 없다고 가정합니다. 일반적으로 그렇습니다. 그리고 minimumInteritemSpacing최소값만을 나타 내기 때문에 실제 공간을 결정하는 것은 간단하지 않은 것 같습니다 .
Drux

5
실제로 return UIEdgeInsetsMake(0, 100, 0, 100);. 추가 공간이있는 경우 컬렉션보기가 중간 간격을 확장하므로 LeftInset + CellWidth * Ncells + CellSpacing * (Ncells-1) + RightInset = CollectionViewWidth
vish

83

중앙 정렬, 동적 너비 collectionview 셀 에 대한 솔루션을 찾는 사람들을 위해 왼쪽 정렬 버전 에 대한 Angel의 대답을 수정하여 UICollectionViewFlowLayout.

CenterAlignedCollectionViewFlowLayout

// NOTE: Doesn't work for horizontal layout!
class CenterAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let superAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
        // Copy each item to prevent "UICollectionViewFlowLayout has cached frame mismatch" warning
        guard let attributes = NSArray(array: superAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }
        
        // Constants
        let leftPadding: CGFloat = 8
        let interItemSpacing = minimumInteritemSpacing
        
        // Tracking values
        var leftMargin: CGFloat = leftPadding // Modified to determine origin.x for each item
        var maxY: CGFloat = -1.0 // Modified to determine origin.y for each item
        var rowSizes: [[CGFloat]] = [] // Tracks the starting and ending x-values for the first and last item in the row
        var currentRow: Int = 0 // Tracks the current row
        attributes.forEach { layoutAttribute in
            
            // Each layoutAttribute represents its own item
            if layoutAttribute.frame.origin.y >= maxY {
                
                // This layoutAttribute represents the left-most item in the row
                leftMargin = leftPadding
                
                // Register its origin.x in rowSizes for use later
                if rowSizes.count == 0 {
                    // Add to first row
                    rowSizes = [[leftMargin, 0]]
                } else {
                    // Append a new row
                    rowSizes.append([leftMargin, 0])
                    currentRow += 1
                }
            }
            
            layoutAttribute.frame.origin.x = leftMargin
            
            leftMargin += layoutAttribute.frame.width + interItemSpacing
            maxY = max(layoutAttribute.frame.maxY, maxY)
            
            // Add right-most x value for last item in the row
            rowSizes[currentRow][1] = leftMargin - interItemSpacing
        }
        
        // At this point, all cells are left aligned
        // Reset tracking values and add extra left padding to center align entire row
        leftMargin = leftPadding
        maxY = -1.0
        currentRow = 0
        attributes.forEach { layoutAttribute in
            
            // Each layoutAttribute is its own item
            if layoutAttribute.frame.origin.y >= maxY {
                
                // This layoutAttribute represents the left-most item in the row
                leftMargin = leftPadding
                
                // Need to bump it up by an appended margin
                let rowWidth = rowSizes[currentRow][1] - rowSizes[currentRow][0] // last.x - first.x
                let appendedMargin = (collectionView!.frame.width - leftPadding  - rowWidth - leftPadding) / 2
                leftMargin += appendedMargin
                
                currentRow += 1
            }
            
            layoutAttribute.frame.origin.x = leftMargin
            
            leftMargin += layoutAttribute.frame.width + interItemSpacing
            maxY = max(layoutAttribute.frame.maxY, maxY)
        }
        
        return attributes
    }
}

CenterAlignedCollectionViewFlowLayout


3
이는 플로우 레이아웃 하위 클래스 xxxx.CustomCollectionViewFlowLayout이 UICollectionViewFlowLayout에서 반환 한 속성을 복사하지 않고 수정하고 있기 때문에 다음과 같은 경고를 표시했습니다. 수정하려면 [ stackoverflow.com/a/36315664/1067951] 의 코드를 사용했습니다 .guard let superArray = super.layoutAttributesForElementsInRect(rect) else { return nil } guard let attributes = NSArray(array: superArray, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }
Ram

게시 해 주셔서 감사합니다. 이것을 달성하기 위해 며칠을 보냈습니다!
양 5

직업과 결과를 커뮤니티와 공유해 주셔서 감사합니다. 코드를 업데이트했습니다. 우선 @Ram이 추천하는 변경 사항을 추가했습니다. 둘째, interItemSpacing기본값을 사용하도록 변경 합니다 minimumInteritemSpacing.
kelin

@Ram 디버거에서 이것을 잡기 위해 UICollectionViewFlowLayoutBreakForInvalidSizes에서 심볼릭 중단 점을 만드는 문제가 있습니다. UICollectionViewFlowLayout의 동작은 다음과 같은 이유로 정의되지 않았습니다. 항목 너비는 UICollectionView의 너비에서 섹션 인셋 왼쪽 및 오른쪽 값을 뺀 값에서 콘텐츠 인셋 왼쪽 및 오른쪽 값을 뺀 값보다 작아야합니다
EI Captain v2.0

1
@Jack은 viewDidLoad ()에서 이와 같이 사용합니다. collectionViewName.collectionViewLayout ()는 CenterAlignedCollectionViewFlowLayout =
Mitesh Dobareeya

57

여기에있는 최고의 솔루션은 기본적으로 작동하지 않았으므로 흐름 레이아웃과 하나의 섹션 만있는 가로 스크롤 컬렉션보기에서 작동해야하는 이것을 생각해 냈습니다.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    {
    // Add inset to the collection view if there are not enough cells to fill the width.
    CGFloat cellSpacing = ((UICollectionViewFlowLayout *) collectionViewLayout).minimumLineSpacing;
    CGFloat cellWidth = ((UICollectionViewFlowLayout *) collectionViewLayout).itemSize.width;
    NSInteger cellCount = [collectionView numberOfItemsInSection:section];
    CGFloat inset = (collectionView.bounds.size.width - (cellCount * cellWidth) - ((cellCount - 1)*cellSpacing)) * 0.5;
    inset = MAX(inset, 0.0);
    return UIEdgeInsetsMake(0.0, inset, 0.0, 0.0);
    }

cellSpacing에 cellCount가 아닌 'cellCount-1'을 곱해야합니다. 셀이 하나 뿐인 경우, 셀 사이에 공간이없는 경우 더욱 분명해집니다.
Fábio Oliveira

그리고 'inset = MAX (inset, 0.0);'에서 사용할 minimumInset도 있어야합니다. 선. 'inset = MAX (inset, minimumInset);'처럼 보입니다. 오른쪽에도 동일한 minimumInset을 적용해야합니다. 컬렉션 뷰 보면 훨씬 더 그들이 잘 :) 가장자리로 확장 할 때
파 비우 올리베이라

이 솔루션과 @Sion의 솔루션은 셀 크기가 일정하면 똑같이 잘 작동합니다. 누구든지 세포의 크기를 얻는 방법을 알고 있습니까? cellForItemAtIndexPath 및 visibleCells는 collectionView가 표시 될 때만 정보를 반환하는 것처럼 보이며 수명주기의이 시점에서는 그렇지 않습니다.
Dan Loughney 2015 년

@Siegfoult 나는 이것을 구현했고 왼쪽으로 스크롤하려고 할 때까지 중간으로 돌아올 때까지 잘 작동했습니다.
Anirudha Mahale

34

인세 트를 동적으로 계산하는 것은 쉽습니다.이 코드는 항상 셀을 중앙에 배치합니다.

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];
}

4
당신의 수정은 무엇입니까?
Siriss

4
@Siriss 대신 SMEPGiPadViewControllerCellWidth 당신은 collectionViewLayout.itemSize.width을 사용할 수 있으며, 이것은 아마도했다 그를, 수정
마이클 Zaborowski

21

다른 답변과 마찬가지로 이것은 정적 크기의 셀에서 작동해야하는 동적 답변입니다. 내가 만든 한 가지 수정 사항은 패딩을 양쪽에 넣는 것입니다. 이렇게하지 않으면 문제가 발생했습니다.


목표 -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);
}

빠른

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)
}

또한 여전히 문제가 발생하는 경우 이러한 값이 서로 관련되어있는 것처럼 보이기 때문에 minimumInteritemSpacing및 모두 minimumLineSpacing동일한 값으로 설정해야합니다 .

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
flowLayout.minimumInteritemSpacing = 20.0;
flowLayout.minimumLineSpacing = 20.0;

나를 위해 충돌
Supertecnoboff

ObjC 객체에서 collectionViewLayout은 직접 사용됩니다. itemSize 및 minimumInteritemSpacing에 액세스 할 수 있도록 UICollectionViewFlowLayout로 주조되어야합니다
buttcmd

19

이것을 컬렉션 뷰 델리게이트에 넣으십시오. 다른 답변보다 기본 흐름 레이아웃 설정을 더 많이 고려하므로 더 일반적입니다.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    NSInteger cellCount = [collectionView.dataSource collectionView:collectionView numberOfItemsInSection:section];
    if( cellCount >0 )
    {
        CGFloat cellWidth = ((UICollectionViewFlowLayout*)collectionViewLayout).itemSize.width+((UICollectionViewFlowLayout*)collectionViewLayout).minimumInteritemSpacing;
        CGFloat totalCellWidth = cellWidth*cellCount + spacing*(cellCount-1);
        CGFloat contentWidth = collectionView.frame.size.width-collectionView.contentInset.left-collectionView.contentInset.right;
        if( totalCellWidth<contentWidth )
        {
            CGFloat padding = (contentWidth - totalCellWidth) / 2.0;
            return UIEdgeInsetsMake(0, padding, 0, padding);
        }
    }
    return UIEdgeInsetsZero;
}

신속한 버전 (g0ld2k에게 감사) :

extension CommunityConnectViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {

    // Translated from Objective-C version at: http://stackoverflow.com/a/27656363/309736

        let cellCount = CGFloat(viewModel.getNumOfItemsInSection(0))

        if cellCount > 0 {
            let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
            let cellWidth = flowLayout.itemSize.width + flowLayout.minimumInteritemSpacing
            let totalCellWidth = cellWidth*cellCount + spacing*(cellCount-1)
            let contentWidth = collectionView.frame.size.width - collectionView.contentInset.left - collectionView.contentInset.right

            if (totalCellWidth < contentWidth) {
                let padding = (contentWidth - totalCellWidth) / 2.0
                return UIEdgeInsetsMake(0, padding, 0, padding)
            }
        }

        return UIEdgeInsetsZero
    }
}

3
실제로 UICollectionViewDelegateFlowLayout에 있습니다. 감사합니다. 이것은 한 행 또는 여러 행인 여러 셀에서 작동합니다. 다른 답변 중 일부는 그렇지 않습니다.
Gene De Lisa

2
이것을 Swift로 변환하고 훌륭하게 작동합니다! 여기에서 Swift 버전을 사용할 수 있습니다 .
g0ld2k

1
이것은 매우 정확하지, totalCellWidth해야한다 cellWidth*cellCount + spacing*(cellCount-1).
James P

1
이러한 "솔루션"을 여러 번 시도한 후 이것은 swift3.0에 대해 약간 수정 한 후 올바르게 작동 할 수있는 유일한 솔루션이었습니다
kemicofa ghost

@rugdealer swift3 변경 사항을 반영하기 위해 답변을 수정 하시겠습니까?
bert

11

왼쪽과 오른쪽에 패딩이 필요한 정적 크기의 컬렉션 뷰 셀에 대한 내 솔루션-

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)
}

9

내 앱에 UICollectionView& 를 사용하는 태그 막대가 UICollectionViewFlowLayout있으며 한 행의 셀이 가운데 정렬됩니다.

올바른 들여 쓰기를 얻으려면 모든 셀의 전체 너비 (간격 포함)를의 너비에서 빼고 UICollectionView2로 나눕니다.

[........Collection View.........]
[..Cell..][..Cell..]
                   [____indent___] / 2

=

[_____][..Cell..][..Cell..][_____]

문제는이 기능입니다.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;

전에 호출되었습니다 ...

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

... 따라서 총 너비를 결정하기 위해 셀을 반복 할 수 없습니다.

대신 각 셀의 너비를 다시 계산해야합니다. 필자의 경우 [NSString sizeWithFont: ... ]셀 너비는 UILabel 자체에 의해 결정되므로 사용합니다.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    CGFloat rightEdge = 0;
    CGFloat interItemSpacing = [(UICollectionViewFlowLayout*)collectionViewLayout minimumInteritemSpacing];

    for(NSString * tag in _tags)
        rightEdge += [tag sizeWithFont:[UIFont systemFontOfSize:14]].width+interItemSpacing;

    // To center the inter spacing too
    rightEdge -= interSpacing/2;

    // Calculate the inset
    CGFloat inset = collectionView.frame.size.width-rightEdge;

    // Only center align if the inset is greater than 0
    // That means that the total width of the cells is less than the width of the collection view and need to be aligned to the center.
    // Otherwise let them align left with no indent.

    if(inset > 0)
        return UIEdgeInsetsMake(0, inset/2, 0, 0);
    else
        return UIEdgeInsetsMake(0, 0, 0, 0);
}

어떻게 얻 interSpacing습니까?
Drux

interSpacingUICollectionViewCells 사이의 공간을 결정하는 내 코드의 상수 일뿐입니다.
Siôn

1
그러나이 공간은 다를 수 있습니다. FWIK는 최소값 만 가정 / 설명 할 수 있습니다.
Drux

8

Swift에서 다음은 각 셀의 측면에 올바른 양의 패딩을 적용하여 셀을 균등하게 분배합니다. 물론, 먼저 셀 너비를 알고 / 설정해야합니다.

func collectionView(collectionView: UICollectionView,
    layout collectionViewLayout: UICollectionViewLayout,
    insetForSectionAtIndex section: Int) -> UIEdgeInsets {

        var cellWidth : CGFloat = 110;

        var numberOfCells = floor(self.view.frame.size.width / cellWidth);
        var edgeInsets = (self.view.frame.size.width - (numberOfCells * cellWidth)) / (numberOfCells + 1);

        return UIEdgeInsetsMake(0, edgeInsets, 60.0, edgeInsets);
}

행 간격을 추가하려면 60.0 대신 0을 변경하십시오.
oscar castellon

8

Swift 2.0 잘 작동합니다!

 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);
    }

여기서 screenWight가 :. : - screenWight하자 : 기본적으로는 내 컬렉션의 폭 (전체 화면 폭은) 내가 상수 만든 CGFloat = UIScreen.mainScreen ()가 bounds.width를 self.view.bounds 쇼 때문에 모든 시간 600 - SizeClasses의의 사촌 요소 - 셀 배열 50- 수동 셀 너비 10- 셀 사이의 거리


1
UICollectionView의 크기를 조정하려는 경우에만 screenWight 대신 collectionView.frame.size.width를 사용할 수 있습니다.
Oscar


2

이것이 Xcode 5에서 새로운 것인지 여부는 확실하지 않지만 이제 Interface Builder를 통해 Size Inspector를 열고 거기에 삽입을 설정할 수 있습니다. 이렇게하면이 작업을 수행하기 위해 사용자 지정 코드를 작성하지 않아도되고 적절한 오프셋을 찾는 속도가 대폭 향상됩니다.


1

이를 수행하는 또 다른 방법은 다음 collectionView.center.x과 같이를 설정하는 것입니다.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let
        collectionView = collectionView,
        layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
    {
        collectionView.center.x = view.center.x + layout.sectionInset.right / 2.0
    }
}

이 경우 나에게 맞는 올바른 삽입물만을 존중합니다.


1

user3676011 답변을 기반으로 작은 수정으로 더 자세한 답변을 제안 할 수 있습니다. 이 솔루션은 Swift 2.0에서 잘 작동합니다 .

enum CollectionViewContentPosition {
    case Left
    case Center
}

var collectionViewContentPosition: CollectionViewContentPosition = .Left

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
    insetForSectionAtIndex section: Int) -> UIEdgeInsets {

    if collectionViewContentPosition == .Left {
        return UIEdgeInsetsZero
    }

    // Center collectionView content

    let itemsCount: CGFloat = CGFloat(collectionView.numberOfItemsInSection(section))
    let collectionViewWidth: CGFloat = collectionView.bounds.width

    let itemWidth: CGFloat = 40.0
    let itemsMargin: CGFloat = 10.0

    let edgeInsets = (collectionViewWidth - (itemsCount * itemWidth) - ((itemsCount-1) * itemsMargin)) / 2

    return UIEdgeInsetsMake(0, edgeInsets, 0, 0)
}

에 문제가있었습니다

(CGFloat (elements.count) * 10))

추가로 -1언급 되어야하는 곳 .


1

이것이 고정 된 항목 간 간격으로 가운데 정렬 된 컬렉션 뷰를 얻은 방법입니다.

#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

1

Swift 3.0을 사용하는 kgaidis의 Objective C 답변 작업 버전 :

let flow = UICollectionViewFlowLayout()

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {        
    let numberOfItems = collectionView.numberOfItems(inSection: 0)
    let combinedItemWidth:CGFloat = (CGFloat(numberOfItems) * flow.itemSize.width) + ((CGFloat(numberOfItems) - 1) * flow.minimumInteritemSpacing)
    let padding = (collectionView.frame.size.width - combinedItemWidth) / 2

    return UIEdgeInsetsMake(0, padding, 0, padding)
}

1

다음은 몇 가지 가정을 가진 내 솔루션입니다.

  • 섹션이 하나뿐입니다
  • 왼쪽 및 오른쪽 삽입이 동일합니다.
  • 셀 높이가 동일

필요에 따라 자유롭게 조정하십시오.

가변 셀 너비가있는 중앙 레이아웃 :

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
    }
}

결과:

여기에 이미지 설명 입력


0

방법은 다음과 같습니다. 잘 작동합니다.

func refreshCollectionView(_ count: Int) {
    let collectionViewHeight = collectionView.bounds.height
    let collectionViewWidth = collectionView.bounds.width
    let numberOfItemsThatCanInCollectionView = Int(collectionViewWidth / collectionViewHeight)
    if numberOfItemsThatCanInCollectionView > count {
        let totalCellWidth = collectionViewHeight * CGFloat(count)
        let totalSpacingWidth: CGFloat = CGFloat(count) * (CGFloat(count) - 1)
        // leftInset, rightInset are the global variables which I am passing to the below function
        leftInset = (collectionViewWidth - CGFloat(totalCellWidth + totalSpacingWidth)) / 2;
        rightInset = -leftInset
    } else {
        leftInset = 0.0
        rightInset = -collectionViewHeight
    }
    collectionView.reloadData()
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsetsMake(0, leftInset, 0, rightInset)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.