Jak programowo ustawić szerokość i wysokość UICollectionViewCell

109

Próbuję zaimplementować CollectionView. Kiedy używam Autolayout, moje komórki nie zmieniają rozmiaru, ale ich wyrównanie.

Teraz wolałabym zmienić ich rozmiary na np

//var size = CGSize(width: self.view.frame.width/10, height: self.view.frame.width/10)

Spróbowałem umieścić w moim CellForItemAtIndexPath

collectionCell.size = size

to jednak nie zadziałało.

Czy jest sposób, aby to osiągnąć?

edytuj :

Wygląda na to, że odpowiedzi zmienią tylko samą szerokość i wysokość CollectionView. Czy istnieje konflikt w ograniczeniach? Jakieś pomysły na to?

JVS
źródło

Odpowiedzi:

282

Użyj tej metody, aby ustawić niestandardową szerokość wysokości komórki.

Pamiętaj, aby dodać te protokoły

UICollectionViewDelegate

UICollectionViewDataSource

UICollectionViewDelegateFlowLayout

Jeśli używasz Swift 5 lub Xcode 11 i później trzeba ustawić Estimate Sizedo nonekorzystania storyboard, aby to działało poprawnie. Jeśli tego nie ustawisz, poniższy kod nie będzie działał zgodnie z oczekiwaniami.

wprowadź opis obrazu tutaj

Swift 4 lub później

extension YourViewController: UICollectionViewDelegate {
    //Write Delegate Code Here
}

extension YourViewController: UICollectionViewDataSource {
    //Write DataSource Code Here
}

extension YourViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: screenWidth, height: screenWidth)
    }
}

Cel C

@interface YourViewController : UIViewController<UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(CGRectGetWidth(collectionView.frame), (CGRectGetHeight(collectionView.frame)));
}
PinkeshGjr
źródło
1
@RamyAlZuhouri Edytował moją odpowiedź, sprawdź i daj mi znać, jeśli nadal musimy to wyjaśnić
PinkeshGjr
8
Jestem tak przyzwyczajony do robienia wszystkiego programowo, że wydaje mi się to teraz tak obce. W scenorysie ustawienie Estimated Size to None i dodanie UICollectionViewDelegateFlowLayout jest tym, co załatwia sprawę
Lance Samaria
5
Ustawienie Estimate Size na none zadziałało dla mnie. Dziękuję @PinkeshGjr
Mohan
5
Cieszę się, że to znalazłem, zmarnowałem 2 godziny na pogoń za tym tylko po to, aby nauczyć się magicznego
``
3
ustaw Estimate Size na none oszczędził moje dni.
tounaobun
73

Pamiętaj, aby dodać protokół UICollectionViewDelegateFlowLayoutw swojej classdeklaracji

class MyCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout
{
    //MARK: - UICollectionViewDelegateFlowLayout

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    {
       return CGSize(width: 100.0, height: 100.0)
    }
}
pierre23
źródło
Nie zapomnij użyć układu przepływu, a nie niestandardowego. Wtedy twój jest jedynym, który pracował dla mnie. Dzięki!
Sean
jak możemy tutaj podać wysokość dynamiczną, podając wysokość statyczną jako 100.0
sangavi
67

Jeśli ktoś korzysta ze scenorysu i przesłania UICollectionViewDelegateFlowLayout, wówczas w Swift 5 i Xcode 11 również ustaw Szacunkowy rozmiar na Brak wprowadź opis obrazu tutaj

Szpieg
źródło
2
To było pomocne. Dzięki!!
Naval Hasan
3
Ustawienie szacunkowego rozmiaru na none naprawiło wszystko
MMK
2
Dzięki ustawieniu szacunkowego rozmiaru Thnx na brak, naprawiono problem
Salman500
2
Dobra poprawka. Oto moja diagnoza tego problemu według dokumentacji Apple : UICollectionViewFlowLayoutwydaje się domyślnie estimatedItemSizedo UICollectionViewFlowLayout.automaticSizeprzy użyciu IB mimo że dokumentacja mówi, powinien on domyślnie CGSizeZero. Jak stwierdza Apple, automaticSize„włącza samodopasowanie rozmiaru komórek do widoku kolekcji”. Dlatego inne zmiany rozmiaru w IB nic nie robią.
Andrew Kirna
1
właśnie zaoszczędził mi godziny, próbowałem znaleźć rozwiązanie od zawsze
Ray Ray
20

Wreszcie otrzymałem odpowiedź. Powinieneś rozszerzyć UICollectionViewDelegateFlowLayout
To powinno działać z odpowiedziami powyżej.

saburo
źródło
Czy mógłbyś napisać przykład?
Mamdouh El Nakeeb
Właściwie zrobiłem rozszerzenie z protokołami zarówno źródła danych, jak i delegateFlowLayout i nie zadziałało. Udało się oddzielić część FlowLayout na jej rozszerzenie. Nie wiem dlaczego, ale zadziałało w szybkim 3.
Skywalker
15

Swift 4.1

Masz 2 sposoby, aby zmienić rozmiar CollectionView.
Pierwszy sposób -> dodaj ten protokół UICollectionViewDelegateFlowLayout
for W moim przypadku chcę podzielić komórkę na 3 części w jednej linii. Zrobiłem ten kod poniżej

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource ,UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    {
            // In this function is the code you must implement to your code project if you want to change size of Collection view
            let width  = (view.frame.width-20)/3
            return CGSize(width: width, height: width)
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return collectionData.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)
        if let label = cell.viewWithTag(100) as? UILabel {
            label.text = collectionData[indexPath.row]
        }
        return cell
    }
}

Drugi sposób -> nie musisz dodawać UICollectionViewDelegateFlowLayout, ale zamiast tego musisz napisać kod w funkcji viewDidload jako kod poniżej

class ViewController: UIViewController {
@IBOutlet weak var collectionView1: UICollectionView!
        var collectionData = ["1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "10.", "11.", "12."]

    override func viewDidLoad() {
        super.viewDidLoad()
        let width = (view.frame.width-20)/3
        let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        layout.itemSize = CGSize(width: width, height: width) 
    }
}


extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return collectionData.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)
        if let label = cell.viewWithTag(100) as? UILabel {
            label.text = collectionData[indexPath.row]
        }

        return cell
    }
}

Cokolwiek napiszesz jako pierwszy lub drugi sposób, otrzymasz taki sam wynik jak powyżej. Napisałem to. U mnie to zadziałało

wprowadź opis obrazu tutaj

Sup.Ia
źródło
2
Prosto z raywenderlich.com 😂
Andrew Kirna
12

Stosunek rozmiaru w zależności od rozmiaru iPhone'a:

Oto, co możesz zrobić, aby mieć różną szerokość i wysokość komórek w odniesieniu do rozmiaru iPhone'a:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    let width = (self.view.frame.size.width - 12 * 3) / 3 //some width
    let height = width * 1.5 //ratio
    return CGSize(width: width, height: height)
}

A może powinieneś również wyłączyć ograniczenia Autoukładu w komórce, aby ta odpowiedź zadziałała.

AnthoPak
źródło
Wypróbowałem to i wszystkie powyższe rozwiązania, ale zawartość w komórce nie zmienia się automatycznie, jak mogę to naprawić
Biasi Wiga
7

Widok kolekcji zawiera obiekt układu . W twoim przypadku jest to prawdopodobnie układ przepływu ( UICollectionViewFlowLayout ). Ustaw właściwość układu przepływu itemSize.

matowe
źródło
Próbowałem tego. daje mi ten sam problem, co wcześniej. w jakiś sposób moja CollectionView zmienia rozmiar zamiast moich komórek.
JVS,
2
Cóż, wszystko zależy od tego, kiedy to zrobisz. Nie pokazałeś swojego prawdziwego kodu, więc kto wie, co robisz? Zapewniam, że to działa.
mat.
7

w Swift3 i Swift4 możesz zmienić rozmiar komórki, dodając UICollectionViewDelegateFlowLayout i implementując to tak:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 100, height: 100)
    }

lub jeśli programowo utworzysz UICollectionView, możesz to zrobić w ten sposób:

    let layout = UICollectionViewFlowLayout()
                    layout.scrollDirection = .horizontal //this is for direction
                    layout.minimumInteritemSpacing = 0 // this is for spacing between cells
                    layout.itemSize = CGSize(width: view.frame.width, height: view.frame.height) //this is for cell size
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
Mohsen mokhtari
źródło
4

swift4 swift 4 widok kolekcji ios przykład Collectionview najnowszy kod roboczy xcode

Dodaj to w sekcji Delegat na górze

UICollectionViewDelegateFlowLayout

i użyj tej funkcji

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = (self.view.frame.size.width - 20) / 3 //some width
    let height = width * 1.5 //ratio
    return CGSize(width: width, height: height)
}

///// przykładowy pełny kod

utwórz w widoku kolekcji i komórce widoku kolekcji w scenorysie podaj odniesienie do kolekcji jako
@IBOutlet słaby var cvContent: UICollectionView!

wklej to w widoku kontrolera

 import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    var arrVeg = [String]()
    var arrFruits = [String]()
    var arrCurrent = [String]()
    
    @IBOutlet weak var cvContent: UICollectionView!
    
  
    
    override func viewDidLoad() {
        super.viewDidLoad()
        arrVeg = ["Carrot","Potato", "Tomato","Carrot","Potato", "Tomato","Carrot","Potato", "Tomato","Carrot","Potato", "Tomato"]
        
        arrVeg = ["Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange"]
        
        
        arrCurrent = arrVeg
    }
    //MARK: - CollectionView
    


    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = (self.view.frame.size.width - 20) / 3 //some width
        let height = width * 1.5 //ratio
        return CGSize(width: width, height: height)
    }
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {

        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        
        return arrCurrent.count
    }
    
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ContentCollectionViewCell
        cell.backgroundColor =  UIColor.green
        return cell
    }
}
Ullas Pujary
źródło
2

Wypróbuj poniższą metodę

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    return CGSize(width: 100.0, height: 100.0)
}
Nilesh Patel
źródło
2

Swift 5 programowo

lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal

        //Provide Width and Height According to your need
        let cellWidth = UIScreen.main.bounds.width / 10
        let cellHeight = UIScreen.main.bounds.height / 10
        layout.itemSize = CGSize(width: cellWidth, height: cellHeight)

        //You can also provide estimated Height and Width
        layout.estimatedItemSize = CGSize(width: cellWidth, height: cellHeight)

        //For Setting the Spacing between cells
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0

        return UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    }()
Shubham Mishra
źródło
1
**Swift 5**
To make this work you have to do the following.

Add these protocols

 - UICollectionViewDelegate


 - UICollectionViewDataSource


 - UICollectionViewDelegateFlowLayout

Your code will then look like this

extension YourViewController: UICollectionViewDelegate {
    //Write Delegate Code Here
}

extension YourViewController: UICollectionViewDataSource {
    //Write DataSource Code Here
}

extension YourViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: screenWidth, height: screenWidth)
    }
}

Now the final and crucial step to see this take effect is to go to your viedDidLoad function inside your Viewcontroller.

    override func viewDidLoad() {
        super.viewDidLoad()
        collection.dataSource = self // Add this
        collection.delegate = self // Add this

        // Do any additional setup after loading the view.
    }

Without telling your view which class the delegate is it won't work.
Taka
źródło
1

# Absolutnie prosty sposób:

class YourCollection: UIViewController,
     UICollectionViewDelegate,
     UICollectionViewDataSource {

# Musisz dodać „UICollectionViewDelegateFlowLayout” lub nie ma autouzupełniania:

class YourCollection: UIViewController,
     UICollectionViewDelegate,
     UICollectionViewDataSource,
     UICollectionViewDelegateFlowLayout {

#Type „sizeForItemAt ...”. Jesteś skończony!

class YourCollection: UIViewController,
     UICollectionViewDelegate,
     UICollectionViewDataSource,
     UICollectionViewDelegateFlowLayout {
 
     func collectionView(_ collectionView: UICollectionView,
      layout collectionViewLayout: UICollectionViewLayout,
      sizeForItemAt indexPath: IndexPath) -> CGSize {
     
     return CGSize(width: 37, height: 63)
}

Otóż ​​to.

Na przykład, jeśli chcesz, aby „każda komórka wypełniała cały widok kolekcji”:

     guard let b = view.superview?.bounds else { .. }
     return CGSize(width: b.width, height: b.height)
Fattie
źródło
0

Innym sposobem jest ustawienie wartości bezpośrednio w układzie przepływu

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: size, height: size)
Antzi
źródło
0

Musisz więc ustawić z storyboardu dla atrybutu dla collectionView w sekcji komórki oszacowanie Size na none, aw swoim ViewController potrzebujesz metody delegata do implementacji tej metody: optional func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize

dgalluccio
źródło
0

Spróbuj użyć metody UICollectionViewDelegateFlowLayout. W Xcode 11 lub nowszym musisz ustawić Szacowany rozmiar na żaden z serii ujęć.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: 
UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let padding: CGFloat =  170
    let collectionViewSize = advertCollectionView.frame.size.width - padding
    return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
Muhammad Shariq Hasnain
źródło
0

Prosty sposób:

Jeśli potrzebujesz tylko prostego stałego rozmiaru :

class SizedCollectionView: UIICollectionView {
    override func common() {
        super.common()
        let l = UICollectionViewFlowLayout()
        l.itemSize = CGSize(width: 42, height: 42)
        collectionViewLayout = l
    }
}

To wszystko.

W scenorysie po prostu zmień klasę z UICollectionView na SizedCollectionView.

Ale !!!

Zwróć uwagę, że klasa bazowa to „UI 'I' CollectionView”. „I” dla inicjatora.

Nie jest łatwo dodać inicjator do widoku kolekcji. Oto typowe podejście:

Widok kolekcji ... z inicjatorem:

import UIKit

class UIICollectionView: UICollectionView {
    private var commoned: Bool = false
    
    override func didMoveToWindow() {
        super.didMoveToWindow()
        if window != nil && !commoned {
            commoned = true
            common()
        }
    }
    
    internal func common() {
    }
}

W większości projektów potrzebny jest „widok kolekcji z inicjatorem”. Więc prawdopodobnie i tak będziesz miał UIICollectionView(zwróć uwagę na dodatkowe I dla inicjatora!) W swoim projekcie.

Fattie
źródło
0

Swift 5, Programmatic UICollectionView ustawianie szerokości i wysokości komórki

// MARK: MyViewController

final class MyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
        let layout = UICollectionViewFlowLayout()
        let spacing: CGFloat = 1
        let numOfColumns: CGFloat = 3
        let itemSize: CGFloat = (UIScreen.main.bounds.width - (numOfColumns - spacing) - 2) / 3
        layout.itemSize = CGSize(width: itemSize, height: itemSize)
        layout.minimumInteritemSpacing = spacing
        layout.minimumLineSpacing = spacing
        layout.sectionInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
        return layout
    }()
    
    private lazy var collectionView: UICollectionView = {
        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: collectionViewLayout)
        collectionView.backgroundColor = .white
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        return collectionView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureCollectionView()
    }
    
    private func configureCollectionView() {
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
        ])
        collectionView.register(PhotoCell.self, forCellWithReuseIdentifier: "PhotoCell")
    }
    
    // MARK: UICollectionViewDataSource

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCell
        cell.backgroundColor = .red
        return cell
    }
    
}

// MARK: PhotoCell

final class PhotoCell: UICollectionViewCell {
    
    lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.layer.masksToBounds = true
        return imageView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init?(coder:) not implemented")
    }
    
    func setupViews() {
        addSubview(imageView)
        NSLayoutConstraint.activate([
            topAnchor.constraint(equalTo: topAnchor),
            bottomAnchor.constraint(equalTo: bottomAnchor),
            leadingAnchor.constraint(equalTo: leadingAnchor),
            trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    }
    
}
Suhit Patil
źródło
0

Jeśli, tak jak ja, trzeba zachować swój układ niestandardowy przepływu jest itemSizedynamicznie aktualizowana w oparciu o szerokości kolekcji widoku danych, należy zastąpić swój UICollectionViewFlowLayout„s prepare()metody. Oto wideo WWDC demonstrujące tę technikę .

class MyLayout: UICollectionViewFlowLayout {
    
    override func prepare() {
        super.prepare()
        
        guard let collectionView = collectionView else { return }
        
        itemSize = CGSize(width: ..., height: ...)
    }
    
}
mattsven
źródło
-4

To jest moja wersja, znajdź odpowiedni stosunek, aby uzyskać rozmiar komórki zgodnie z wymaganiami.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
return CGSizeMake(CGRectGetWidth(collectionView.frame)/4, CGRectGetHeight(collectionView.frame)/4); 
} 
Rahul K Rajan
źródło