【问题标题】:UICollectionView cells do not take up width of UICollectionViewUICollectionView 单元格不占用 UICollectionView 的宽度
【发布时间】:2015-08-25 14:23:18
【问题描述】:

UICollectionView 的宽度将因设备而异,因为它被限制为达到视图的水平边距。 collectionView 的布局是 Flow 的。我使用 UICollectionView 的唯一原因是将图像均匀地分布在不同的设备尺寸上,并且所有这些图像都将具有点击手势识别器。并且行数是可变的,因为图像图标的数量会有所不同。显然这是我所追求的方法:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    return CGSize(width: collectionViewWidth/6, height: collectionViewWidth/6);
}

我尝试过这样做:

var collectionViewWidth: CGFloat = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.dataSource = self
}

override func viewDidAppear(animated: Bool) {
    collectionViewWidth = collectionView.collectionViewLayout
        .collectionViewContentSize().width
}

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    return CGSize(width: collectionViewWidth/6, height: collectionViewWidth/6);
}

但我在代码中尝试的任何操作都不会对最终输出产生任何影响(忘记不在 UICollectionView 中的图像 - 它们将被删除):

我在界面生成器中更改了单元格的大小,结果如下:

所以我有两个问题:

为什么上图中的单元格没有到UICollectionView的边缘?

如何在运行时以编程方式设置单元格大小,以使我的 UICollectionView 在所有设备大小上都有 6 列?

这是包含 UICollectionView 的 ViewController 的所有代码:

import UIKit

class DressingRoomViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    let identifier = "cellIdentifier"
    let dataSource = DataSource()
    var collectionViewWidth: CGFloat = 0.0

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.dataSource = self
    }

    override func viewDidAppear(animated: Bool) {
        collectionViewWidth = collectionView.collectionViewLayout
            .collectionViewContentSize().width
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func prepareForSegue(  segue: UIStoryboardSegue,
                                    sender: AnyObject?) {
        if (segue.identifier == "dressingRoom2MyOutfits") {
            let myOutfitsViewController = segue.destinationViewController
                as! MyOutfitsViewController
        }
    }
}



// MARK:- UICollectionViewDataSource Delegate
extension DressingRoomViewController : UICollectionViewDataSource {

    func numberOfSectionsInCollectionView(
        collectionView: UICollectionView) -> Int {
            return dataSource.groups.count
    }

    func collectionView(collectionView: UICollectionView,
        numberOfItemsInSection section: Int) -> Int {
            return dataSource.numbeOfRowsInEachGroup(section)
    }

    func collectionView(collectionView: UICollectionView,
        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
            identifier,forIndexPath:indexPath) as! FruitCell

        let fruits: [Fruit] = dataSource.fruitsInGroup(indexPath.section)
        let fruit = fruits[indexPath.row]

        let name = fruit.name!

        cell.imageView.image = UIImage(named: name)
        cell.caption.text = name.capitalizedString

        return cell
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        return CGSize(width: collectionViewWidth/6, height: collectionViewWidth/6);
    }
}

可能值得一提的是,我的 UICollectionView 的数据源只是因为我在做一个教程而被分组。这是数据源代码:

类数据源{

init() {
    populateData()
}

var fruits:[Fruit] = []
var groups:[String] = []

func numbeOfRowsInEachGroup(index: Int) -> Int {
    return fruitsInGroup(index).count
}

func numberOfGroups() -> Int {
    return groups.count
}

func gettGroupLabelAtIndex(index: Int) -> String {
    return groups[index]
}

// MARK:- Populate Data from plist

func populateData() {
    if let path = NSBundle.mainBundle().pathForResource(
        "Fruits", ofType: "plist") {
            if let dictArray = NSArray(contentsOfFile: path) {
                for item in dictArray {
                    if let dict = item as? NSDictionary {
                        let name = dict["name"] as! String
                        let group = dict["group"] as! String

                        let fruit = Fruit(name: name, group: group)
                        if !contains(groups, group){
                            groups.append(group)
                        }
                        fruits.append(fruit)
                    }
                }
            }
    }
}

// MARK:- FruitsForEachGroup

func fruitsInGroup(index: Int) -> [Fruit] {
    let item = groups[index]
    let filteredFruits = fruits.filter { (fruit: Fruit) -> Bool in
        return fruit.group == item
    }
    return filteredFruits
}

}

这是加载到数据源中的 plist:

编辑:我已经做了一些工作来删除不需要的组。我看到这些组导致新行开始过早。我将代码更改为:

    // MARK:- UICollectionViewDataSource Delegate
extension DressingRoomViewController : UICollectionViewDataSource {

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

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

    func collectionView(collectionView: UICollectionView,
        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
            identifier,forIndexPath:indexPath) as! FruitCell

        let fruits: [Fruit] = dataSource.fruits
        let fruit = fruits[indexPath.row]

        let name = fruit.name!

        cell.imageView.image = UIImage(named: name)
        cell.caption.text = name.capitalizedString

        return cell
    }

    func collectionView(collectionView: UICollectionView,
        sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
            return CGSize(width: 200, height: 200);
    }
}

现在我只需要以某种方式将单元格宽度设为集合视图宽度的 1/6,然后裁剪掉 UICollectioview 的底部,这只是浪费空间

【问题讨论】:

  • 您以前使用过集合视图吗? - 另外,让我问你这个问题:你是否尝试过设置断点或记录以查看事情发生的顺序?如果集合视图设置自己之前你的viewDidAppear: 配置你的宽度值?这会发生吗?
  • @matt 不,我没有。我是 iOS 新手。我已经在 Team Treehouse 上完成了iOS with Swift track,现在我正在自己制作我的第一个应用程序。我只是在设置 collectViewWidth 变量后放置了一个断点,它在 iPhone4 上设置为 288。但是func collectionView(collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { return CGSize(width: 20, height: 20); } 函数永远不会触发断点。也许这不是故意的,就像配置 collectionView 一样。
  • @matt 我已经尝试将值硬编码到上述设置单元格大小的函数中,但它没有改变输出
  • 我建议先了解集合视图,然后再尝试以奇怪的方式弄乱它们。同时,我给出了一个答案,希望能为您指明正确的方向。

标签: ios uicollectionview


【解决方案1】:

如果您使用的是 UICollectionViewFlowLayout,那么您配置单元格宽度的方式是与 UICollectionViewFlowLayout 对话。设置它的itemSize

【讨论】:

    【解决方案2】:

    您可以在viewDidLoad中设置collectionViewLayout的itemSize,如下例所示:

        override func viewDidLoad() {
            super.viewDidLoad()
            let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
            let collectionViewSize = collectionView.frame.size
            let nrOfCellsPerRow = 6 
            let itemWidth = collectionViewSize.width/nrOfCellsPerRow
            let itemHeight = 80 // example
            layout.itemSize = CGSize(width: itemWidth, height: itemHeight) 
        }
    

    如果你想在单元格之间有间距,你将不得不补偿 itemWidth 计算

    【讨论】:

      【解决方案3】:

      对于布局,我们基本上更喜欢创建一个 CustomflowLayout,继承自 UICollectionViewFlowlayout 并覆盖它的 2 个方法:

      • layoutAttributesForElementsInRect:(必填)
      • layoutAttributesForItemAtIndexPath:(必填)

      然后设置

      [self.collectionview setFlowlayout:CustomflowLayout];

      【讨论】: