Bir UICollectionView hücrelerini ortaya nasıl hizalayabilirim?


98

Şu anda UICollectionViewkullanıcı arayüzü ızgarası için kullanıyorum ve iyi çalışıyor. Ancak, yatay kaydırmayı etkinleştirmek istiyorum. Izgara, sayfa başına 8 öğeyi destekler ve toplam öğe sayısı, örneğin 4 olduğunda, öğeler yatay kaydırma yönü etkinken şu şekilde düzenlenmelidir:

0 0 x x
0 0 x x

Burada 0 -> Koleksiyon Öğesi ve x -> Boş Hücreler

Bunları ortaya hizalamanın bir yolu var mı?

x 0 0 x
x 0 0 x

Böylece içerik daha temiz görünüyor?

Ayrıca aşağıdaki düzenleme de beklediğim bir çözüm olabilir.

0 0 0 0
x x x x

Yanıtlar:


80

Tek satırlık görünümü şöyle bir şey uygulayarak elde edebileceğinizi düşünüyorum:

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

İçeriği tek bir satıra nasıl zorlayacağınızı bulmak için bu sayı ile oynamanız gerekecek. İlk 0, üst kenar argümanıdır, içeriği ekranda dikey olarak ortalamak isterseniz bunu da ayarlayabilirsiniz.


1
Bu iyi bir çözüm, teşekkürler, ancak bu benim tarafımdan bazı ekstra hesaplamalar gerektiriyor.
RVN

Bu değerleri nasıl hesaplıyorsunuz?
jjxtra

7
Eklemeleri dinamik olarak hesaplamak için: CGFloat leftInset = (collectionView.frame.size.width-40 * [collectionView numberOfItemsInSection: section]) / 2; 40 = hücrenin boyutu
Abduliam Rehmanius

1
@AbduliamRehmanius Ancak bu, hücrelerin yatay olarak aralıklı olmadığını varsayar. Genellikle öyleler. Ve gerçek alanları belirlemek kolay görünmüyor çünkü minimumInteritemSpacingyalnızca minimum alanlarını gösteriyor.
Drux

5
Aslında olmalı return UIEdgeInsetsMake(0, 100, 0, 100);. Fazladan boşluk varsa, koleksiyon görünümü ara aralığı genişletir, bu nedenle LeftInset + CellWidth * Ncells + CellSpacing * (Ncells-1) + RightInset = CollectionViewWidth
vish

85

Merkeze hizalanmış, dinamik genişlikli koleksiyon görünümü hücrelerine bir çözüm arayanlar için , benim gibi, Angel'ın sola hizalı bir versiyon için yanıtını merkeze hizalanmış bir alt sınıf oluşturmak için değiştirdim 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
Bu, aşağıdaki uyarıyı verdi, ancak Bu, büyük olasılıkla, akış düzeni alt sınıfı xxxx.CustomCollectionViewFlowLayout tarafından UICollectionViewFlowLayout tarafından döndürülen öznitelikleri kopyalamadan değiştirdiği için ortaya çıkmaktadır Düzeltmek için [ stackoverflow.com/a/36315664/1067951] ' deki kodu kullandımguard let superArray = super.layoutAttributesForElementsInRect(rect) else { return nil } guard let attributes = NSArray(array: superArray, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }
Ram

Gönderdiğiniz için teşekkürler. Bunu başarmak için birkaç gün geçirdim!
Mayıs Yang

İşiniz ve sonuçları toplulukla paylaştığınız için teşekkür ederiz. Kodu güncelledim. Öncelikle @Ram tarafından önerilen değişiklikleri ekledim. İkinci olarak, interItemSpacingvarsayılanı kullanmak için değiştiriyorum minimumInteritemSpacing.
kelin

@Ram Bunu hata ayıklayıcıda yakalamak için UICollectionViewFlowLayoutBreakForInvalidSizes'de sembolik bir kesme noktası oluşturma sorunu yaşıyorum. UICollectionViewFlowLayout davranışı tanımlanmamıştır çünkü: öğe genişliği, UICollectionView genişliğinden eksi bölüm girintileri sol ve sağ değerleri eksi içerik girintileri sol ve sağ değerlerinden daha az olmalıdır
EI Captain v2.0

1
@ Jack, viewDidLoad () 'da böyle kullanın. collectionViewName.collectionViewLayout = CenterAlignedCollectionViewFlowLayout ()
Mitesh Dobareeya

57

Buradaki en iyi çözümler benim için kutudan çıkar çıkmaz işe yaramadı, bu yüzden akış düzeni ve yalnızca bir bölüm içeren herhangi bir yatay kaydırma toplama görünümü için çalışması gereken bunu buldum:

- (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'i cellCount ile değil 'cellCount - 1' ile çarpmalısınız. Tek bir hücre olması durumunda daha belirgin hale gelir: hücreler arasında boşluk yoktur.
Fábio Oliveira

Ayrıca 'inset = MAX (iç metin, 0.0);' içinde kullanılacak bir minimumInset olmalıdır. hat. 'İnset = MAX (inset, minimumInset);' gibi görünür. Aynı minimumInset'i sağ tarafa da uygulamalısınız. Koleksiyon görünümleri, kenara kadar
Fábio Oliveira

Hücre boyutu sabitse hem bu çözüm hem de @Sion'dan gelen çözüm eşit derecede iyi çalışır. Hücrelerin boyutunu nasıl alacağını bilen var mı? cellForItemAtIndexPath ve visibleCells, yalnızca collectionView görünür olduğunda bilgi döndürüyor gibi görünüyor - ve yaşam döngüsünün bu noktasında değil.
Dan Loughney

@Siegfoult Bunu uyguladım ve sola kaydırmaya çalışana kadar işe yaradı, ortaya geri geldi.
Anirudha Mahale

34

Ekleri dinamik olarak hesaplamak kolaydır, bu kod her zaman hücrelerinizi ortalayacaktır:

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
Değişikliğiniz neydi?
Siriss

4
SMEPGiPadViewControllerCellWidth yerine @Siriss collectionViewLayout.itemSize.width'i kullanabilirsiniz ve bu muhtemelen onun modifikasyonuydu
Michal Zaborowski

21

Diğer yanıtlara benzer şekilde, bu, statik boyutlu hücreler için çalışması gereken dinamik bir yanıttır. Yaptığım tek değişiklik, dolguyu her iki tarafa da koymam. Bunu yapmazsam sorunlar yaşadım.


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

Hala sorun yaşıyorsanız eğer Ayrıca, her iki set emin olmak minimumInteritemSpacingve minimumLineSpacingbu değerler birbiriyle gibi görünüyor beri aynı değerlere.

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

Sadece benim için çöküyor
Supertecnoboff

ObjC nesne koleksiyonundaViewLayout doğrudan kullanılır. İtemSize ve minimumInteritemSpacing
buttcmd

19

Bunu koleksiyon görünümü temsilcinize ekleyin. Diğer cevaplardan daha fazla temel akış düzeni ayarlarını dikkate alır ve bu nedenle daha geneldir.

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

hızlı sürüm (teşekkürler 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
Aslında UICollectionViewDelegateFlowLayout'unuzda. Teşekkürler, bu, bir satır veya birden çok satır olan bir dizi hücre için işe yarar. Diğer cevaplardan bazıları değil.
Gene De Lisa

2
Bunu Swift'e çevirdim ve harika çalışıyor! Swift versiyonu burada mevcuttur .
g0ld2k

1
Bu tam olarak doğru değil, totalCellWidtholmalı cellWidth*cellCount + spacing*(cellCount-1).
James P

1
Bu "çözümlerin" çoğunu denedikten sonra, onu swift3.0 için biraz değiştirdikten sonra doğru şekilde çalışabildiğim tek çözüm buydu
kemicofa ghost

@rugdealer Swift3 değişikliklerinizi yansıtacak şekilde cevabı değiştirmek ister misiniz?
bert

11

Solda ve sağda dolgusu olması gereken statik boyutlu koleksiyon görünümü hücreleri için çözümüm-

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

Uygulamamda , ortalanmış bir hücre satırı olan UICollectionView& kullanan bir etiket çubuğum var UICollectionViewFlowLayout.

Doğru girintiyi elde etmek için, tüm hücrelerin toplam genişliğini (boşluk dahil) genişliğinizden çıkarır UICollectionViewve ikiye bölersiniz.

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

=

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

Sorun bu işlev -

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

önce çağrılır ...

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

... böylece toplam genişliği belirlemek için hücrelerinizin üzerinde yineleme yapamazsınız.

Bunun yerine, her hücrenin genişliğini yeniden hesaplamanız gerekiyor, benim durumumda, [NSString sizeWithFont: ... ]hücre genişliklerimin UILabel tarafından belirlendiği için kullanıyorum.

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

Nasıl elde edersiniz interSpacing?
Drux

interSpacingkodumda sadece UICollectionViewCells
Siôn

1
Ancak bu boşluk değişebilir: FWIK yalnızca minimum değeri varsayabilir / belirtebilirsiniz.
Drux

8

Swift'de aşağıdakiler, her bir hücrenin kenarlarına doğru miktarda dolgu uygulayarak hücreleri eşit şekilde dağıtacaktır. Tabii ki, önce hücre genişliğinizi bilmeniz / ayarlamanız gerekir.

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

Satır aralığı eklemek için 60.0 yerine 0 değerini değiştirin
oscar castellon

8

Swift 2.0 benim için iyi çalışıyor!

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

Nerede: screenWight : temelde koleksiyonumun genişliği (tam ekran genişliği) - Sabitleri yaptım: let screenWight: CGFloat = UIScreen.mainScreen (). Bounds.width çünkü self.view.bounds her seferinde 600 - SizeClasses öğelerinin coz'unu gösterir - hücre dizisi 50 - benim manuel hücre genişliğim 10 - hücreler arasındaki mesafem


1
Sen UICollectionView yeniden boyutlandırmak istediğiniz sadece durumda, yerine screenWight ait collectionView.frame.size.width kullanabilirsiniz
Oscar


2

Bunun Xcode 5'te yeni olup olmadığından emin değilsiniz, ancak şimdi Boyut Denetçisini Arayüz Oluşturucu aracılığıyla açabilir ve orada bir ek ayarlayabilirsiniz. Bu, sizin için bunu yapmak için özel kod yazmak zorunda kalmanızı engeller ve uygun ofsetleri bulma hızınızı büyük ölçüde artırmalıdır.


1

Bunu yapmanın başka bir yolu da şu şekilde ayarlamaktır 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
    }
}

Bu durumda, sadece benim için çalışan doğru eke saygı duymak.


1

User3676011 cevabına dayanarak, küçük bir düzeltme ile daha ayrıntılı bir tane önerebilirim. Bu çözüm Swift 2.0'da harika çalışıyor .

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

İçinde bir sorun vardı

(CGFloat (elements.count) * 10))

ek olarak nerede -1belirtilmelidir.


1

Sabit bir Interitem aralığı ile ortaya hizalanmış bir koleksiyon görünümünü bu şekilde elde ettim

#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 kullanan kgaidis'in Objective C cevabının çalışma versiyonu:

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

İşte birkaç varsayım içeren çözümüm:

  • sadece bir bölüm var
  • sol ve sağ girintiler eşittir
  • hücre yüksekliği aynı

İhtiyaçlarınızı karşılamak için kendinizi özgürce ayarlayın.

Değişken hücre genişliğine sahip ortalanmış düzen:

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

Sonuç:

görüntü açıklamasını buraya girin


0

İşte bunu nasıl yapabileceğiniz ve iyi çalışıyor

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)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.