วิธีจัดตำแหน่งเซลล์ของ UICollectionView ให้อยู่กึ่งกลาง


98

ฉันกำลังใช้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

1
@AbduliamRehmanius แต่สิ่งนี้ถือว่าเซลล์ไม่ได้เว้นระยะห่างในแนวนอน โดยปกติแล้วจะมี และดูเหมือนว่าจะไม่ตรงไปตรงมาในการกำหนดช่องว่างจริงเพราะminimumInteritemSpacingระบุเฉพาะขั้นต่ำเท่านั้น
Drux

5
return UIEdgeInsetsMake(0, 100, 0, 100);มันจริงที่ควรจะเป็น หากมีพื้นที่เพิ่มเติมมุมมองคอลเลคชันจะขยายระยะห่างระหว่างกาลดังนั้นคุณต้องแน่ใจว่า LeftInset + CellWidth * Ncells + CellSpacing * (Ncells-1) + RightInset = CollectionViewWidth
vish

85

สำหรับผู้ที่มองหาวิธีการแก้ศูนย์ชิดเป็นแบบไดนามิกที่มีความกว้างเซลล์ collectionview ขณะที่ผมกำลังฉันสิ้นสุดการปรับเปลี่ยนคำตอบของแองเจิลสำหรับรุ่นจัดชิดซ้ายเพื่อสร้าง subclass 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 ประการที่สองผมเปลี่ยนไปใช้ค่าเริ่มต้น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 - 1' ไม่ใช่ cellCount จะเห็นได้ชัดเจนมากขึ้นในกรณีที่มีเซลล์เพียงเซลล์เดียว: ไม่มีช่องว่างระหว่างเซลล์
Fábio Oliveira

และควรมีค่าต่ำสุดที่จะใช้ใน 'inset = MAX (inset, 0.0);' ไลน์. ดูเหมือนว่า 'inset = MAX (inset, minimumInset);' คุณควรใช้ขั้นต่ำเดียวกันนั้นทางด้านขวาเช่นกัน มุมมองคอลเลคชันดูดีขึ้นมากเมื่อขยายไปจนถึงขอบ :)
Fábio Oliveira

ทั้งโซลูชันนี้และโซลูชันจาก @Sion ทำงานได้ดีเท่ากันหากขนาดของเซลล์คงที่ มีใครรู้บ้างว่าจะได้ขนาดของเซลล์? cellForItemAtIndexPath และ visibleCells ดูเหมือนจะส่งคืนข้อมูลเมื่อมองเห็น collectionView เท่านั้นและ ณ จุดนี้ในวงจรชีวิตก็ไม่ได้
Dan Loughney

@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 ได้และนี่อาจเป็นการปรับเปลี่ยนของเขา
Michal 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 object collectionViewLayout ถูกใช้โดยตรง ต้องแคสต์ไปที่ UICollectionViewFlowLayout จึงจะสามารถเข้าถึง itemSize และ MinimumInteritemSpacing
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 และใช้งานได้ดี! รุ่นสวิฟท์ที่มีอยู่ที่นี่
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โดยมีแถวหนึ่งของเซลล์อยู่ตรงกลาง

เพื่อให้ได้การเยื้องที่ถูกต้องคุณต้องลบความกว้างรวมของเซลล์ทั้งหมด (รวมระยะห่าง) ออกจากความกว้างของคุณUICollectionViewแล้วหารด้วยสอง

[........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

interSpacingเป็นเพียงค่าคงที่ในรหัสของฉันซึ่งกำหนดช่องว่างระหว่าง UICollectionViewCells
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);
}

หากต้องการเพิ่มระยะห่างระหว่างแถวให้เปลี่ยน 0 แทน 60.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 - coz ขององค์ประกอบ SizeClasses - อาร์เรย์ของเซลล์ 50 - ความกว้างของเซลล์ที่กำหนดเอง 10 - ระยะห่างระหว่างเซลล์


1
คุณสามารถใช้ collectionView.frame.size.width แทน screenWight ได้ในกรณีที่คุณต้องการปรับขนาด UICollectionView
Oscar


2

ไม่แน่ใจว่านี่เป็นสิ่งใหม่ใน Xcode 5 หรือไม่ แต่ตอนนี้คุณสามารถเปิดตัวตรวจสอบขนาดผ่านตัวสร้างอินเทอร์เฟซและตั้งค่าสิ่งที่ใส่เข้าไปได้ ซึ่งจะทำให้คุณไม่ต้องเขียนโค้ดที่กำหนดเองเพื่อทำสิ่งนี้ให้คุณและควรเพิ่มความเร็วอย่างมากในการหาค่าชดเชยที่เหมาะสม


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 ฉันสามารถแนะนำรายละเอียดเพิ่มเติมได้โดยมีการแก้ไขเล็กน้อย การแก้ปัญหานี้ใช้งานได้ดีในสวิฟท์ 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 (จำนวนองค์ประกอบ) * 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

คำตอบ Objective C ของ kgaidis เวอร์ชันที่ใช้งานได้โดยใช้ Swift 3.0:

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.