Jak ustawić odstępy między komórkami i współczynnik rozmiaru UICollectionView - UICollectionViewFlowLayout?

137

Próbuję dodać UICollectionViewdo ViewController, i muszę mieć komórki 3 „w rzędzie” bez pustej przestrzeni pomiędzy komórkami (powinien wyglądać siatce). Szerokość komórki powinna wynosić jedną trzecią rozmiaru ekranu, więc pomyślałem, że layout.itemszerokość powinna być taka sama. Ale wtedy rozumiem:

layout.itemSize width = screenSize.width / 3

Jeśli zmniejszę ten rozmiar (np. O 7 lub 8 pikseli ), będzie lepiej, ale trzecia komórka w rzędzie nie jest całkowicie widoczna i nadal mam tę pustą przestrzeń (góra i dół oraz lewa i prawa).

wprowadź opis obrazu tutaj

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    var collectionView: UICollectionView?
    var screenSize: CGRect!
    var screenWidth: CGFloat!
    var screenHeight: CGFloat!

    override func viewDidLoad() {
        super.viewDidLoad()

        screenSize = UIScreen.mainScreen().bounds
        screenWidth = screenSize.width
        screenHeight = screenSize.height

        // Do any additional setup after loading the view, typically from a nib
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth / 3, height: screenWidth / 3)
        collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
        collectionView!.dataSource = self
        collectionView!.delegate = self
        collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
        collectionView!.backgroundColor = UIColor.greenColor()
        self.view.addSubview(collectionView!)
    }

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as CollectionViewCell
        cell.backgroundColor = UIColor.whiteColor()
        cell.layer.borderColor = UIColor.blackColor().CGColor
        cell.layer.borderWidth = 0.5
        cell.frame.size.width = screenWidth / 3
        cell.frame.size.height = screenWidth / 3

        cell.textLabel?.text = "\(indexPath.section):\(indexPath.row)"
        return cell
    }
}
Marko
źródło
Oto twoje rozwiązanie. Dzięki.
janu kansagra
Oto kod roboczy dla Swift 4 stackoverflow.com/a/54703798/10150796
Nikunj Kumbhani

Odpowiedzi:

299

Dodaj te 2 linie

layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0

Więc masz:

   // Do any additional setup after loading the view, typically from a nib.
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth/3, height: screenWidth/3)
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        collectionView!.collectionViewLayout = layout

To usunie wszystkie spacje i da ci układ siatki:

wprowadź opis obrazu tutaj

Jeśli chcesz, aby pierwsza kolumna miała szerokość równą szerokości ekranu, dodaj następującą funkcję:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    if indexPath.row == 0
    {
        return CGSize(width: screenWidth, height: screenWidth/3)
    }
    return CGSize(width: screenWidth/3, height: screenWidth/3);

}

Układ siatki będzie teraz wyglądał (dodałem też niebieskie tło do pierwszej komórki): wprowadź opis obrazu tutaj

Peter Todd
źródło
Dziękuję bardzo, spodziewałem się czegoś znacznie bardziej skomplikowanego i spędziłem nad tym dużo czasu. Dzięki jeszcze raz.
Marko,
I jeszcze jedno, czy istnieje możliwość zmiany layoutu. ItemSize tylko dla pierwszej komórki? (Wiem, że muszę zmienić rozmiar cell.frame.size, gdy indexPath.row ma wartość 0, ale nie wiem, jak zmienić rozmiar układu tylko dla jednej pozycji) Szerokość pierwszego elementu powinna być równa screenWidth .
Marko,
1
Cześć, mam ten sam problem, ale chcę poznać podobny tytuł funkcji w celu C.
Nada Gamal
1
Ok, teraz działa dobrze, wielkie dzięki :) Funkcja celu C: - (CGSize) collectionView: (UICollectionView ) collectionView layout: (UICollectionViewLayout ) collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath *) indexPath {return CGSizeMake (screenWidth / 3, screenWidth / 3, screenWidth / 3, screenWidth / 3 ); }
Nada Gamal
4
dzięki za świetną odpowiedź @greentor. wysłał nagrodę. Nie zapomnij o collectionView! .CollectionViewLayout = układ , czytelnicy
Fattie
47

W przypadku Swift 3 i XCode 8 to zadziałało. Wykonaj poniższe kroki, aby to osiągnąć: -

{
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    let width = UIScreen.main.bounds.width
    layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    layout.itemSize = CGSize(width: width / 2, height: width / 2)
    layout.minimumInteritemSpacing = 0
    layout.minimumLineSpacing = 0
    collectionView!.collectionViewLayout = layout
}

Umieść ten kod w funkcji viewDidLoad ().

Nirmit Dagly
źródło
32

W niektórych sytuacjach ustawienie UICollectionViewFlowLayoutin viewDidLoadlub ViewWillAppearmoże nie mieć wpływu na collectionView.

Ustawienie UICollectionViewFlowLayoutin viewDidAppearmoże spowodować zobaczenie zmian rozmiarów komórek w czasie wykonywania.

Inne rozwiązanie w Swift 3:

extension YourViewController : UICollectionViewDelegateFlowLayout{

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let collectionViewWidth = collectionView.bounds.width
        return CGSize(width: collectionViewWidth/3, height: collectionViewWidth/3)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }
}
Maor
źródło
Świetna odpowiedź! To zdecydowanie sposób Apple. Tak się składa, że ​​nie wiesz, jak odświeżyć się, gdy urządzenie zmieni orientację, prawda?
jlstr
17

Jeśli szukasz Swift 3, postępuj zgodnie z instrukcjami, aby to osiągnąć:

func viewDidLoad() {

    //Define Layout here
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()

    //Get device width
    let width = UIScreen.main.bounds.width

    //set section inset as per your requirement.
    layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)

    //set cell item size here 
    layout.itemSize = CGSize(width: width / 2, height: width / 2)

    //set Minimum spacing between 2 items
    layout.minimumInteritemSpacing = 0

    //set minimum vertical line spacing here between two lines in collectionview
    layout.minimumLineSpacing = 0

    //apply defined layout to collectionview
    collectionView!.collectionViewLayout = layout

}

Jest to weryfikowane w Xcode 8.0 ze Swift 3.

Dharmendra Kachhadiya
źródło
8
let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.minimumLineSpacing = 8
jamal zare
źródło
4

Szybki 4

let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
collectionViewLayout?.sectionInset = UIEdgeInsetsMake(0, 20, 0, 40) 
collectionViewLayout?.invalidateLayout()
Alfi
źródło
1

W przypadku Swift 3 i XCode 8 to zadziałało. Wykonaj poniższe kroki, aby to osiągnąć: -

viewDidLoad()
    {
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        var width = UIScreen.main.bounds.width
        layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    width = width - 10
    layout.itemSize = CGSize(width: width / 2, height: width / 2)
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        collectionView!.collectionViewLayout = layout
    }
Moin Shirazi
źródło
3
Jak wziąć pod uwagę fakt, jeśli masz wiele sekcji
Nikhil Pandey
to tylko dla 1 kolumny (sekcja)
user924
0

Dla Swift 3+ i Xcode 9+ Spróbuj tego użyć

extension ViewController: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let collectionWidth = collectionView.bounds.width
    return CGSize(width: collectionWidth/3, height: collectionWidth/3)

}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 0

}
Aditya
źródło
0

Swift 5: Aby uzyskać równomiernie rozmieszczone spacje między komórkami z dynamiczną szerokością komórki, aby jak najlepiej wykorzystać przestrzeń kontenera, możesz użyć poniższego fragmentu kodu, podając minimalną wartość CellWidth .

private func collectionViewLayout() -> UICollectionViewLayout {
    
    let layout = UICollectionViewFlowLayout()
    layout.sectionHeadersPinToVisibleBounds = true
    // Important: if direction is horizontal use minimumItemSpacing instead. 
    layout.scrollDirection = .vertical
    
    let itemHeight: CGFloat = 240
    let minCellWidth :CGFloat = 130.0
    let minItemSpacing: CGFloat = 10
    let containerWidth: CGFloat = self.view.bounds.width
    let maxCellCountPerRow: CGFloat =  floor((containerWidth - minItemSpacing) / (minCellWidth+minItemSpacing ))
    
    let itemWidth: CGFloat = floor( ((containerWidth - (2 * minItemSpacing) - (maxCellCountPerRow-1) * minItemSpacing) / maxCellCountPerRow  ) )
    // Calculate the remaining space after substracting calculating cellWidth (Divide by 2 because of left and right insets)
    let inset = max(minItemSpacing, floor( (containerWidth - (maxCellCountPerRow*itemWidth) - (maxCellCountPerRow-1)*minItemSpacing) / 2 ) )

    
    layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
    layout.minimumInteritemSpacing = min(minItemSpacing,inset)
    layout.minimumLineSpacing = minItemSpacing
    layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)

    
    return layout
}
deniz yalcin
źródło