Sweeper
Sweeper

Reputation: 270810

How to create a collection view that shows the user a bunch of cards?

I am writing an iOS card game. I want to show the user the cards in his hand. I looked for CocoaPods that does this in a good-looking way, but couldn't find any, so I decided to create such a view myself using a horizontal UICollectionView.

Here are some requirements:

This is my attempt:

I added constraints for the collection view and the image view in the prototype cell in the storyboard. Then I added this code.

extension ViewController : UICollectionViewDataSource, UICollectionViewDelegate {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        cell.addConstraint(NSLayoutConstraint(item: cell, attribute: .height, relatedBy: .equal, toItem: cell, attribute: .width, multiplier: 5.0 / 7.0, constant: 0))
        // here I am just setting all the cells to have the same image for testing purposes
        (cell.viewWithTag(1) as! UIImageView).image = someCardImage
        return cell
    }
}

Result:

Only the collection view's constraints seem to be working. Everything else isn't. The aspect ratio constraint I added programmatically above apparently conflicts with some other constraint that I am not aware of:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x604000290810 UICollectionViewCell:0x7fd536610300.height == 0.714286*UICollectionViewCell:0x7fd536610300.width   (active)>",
    "<NSLayoutConstraint:0x600000292390 'UIView-Encapsulated-Layout-Height' UICollectionViewCell:0x7fd536610300.height == 99   (active)>", <---- What is this constraint?
    "<NSLayoutConstraint:0x600000292340 'UIView-Encapsulated-Layout-Width' UICollectionViewCell:0x7fd536610300.width == 68   (active)>" <---- What is this constraint?
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600000292390 'UIView-Encapsulated-Layout-Height' UICollectionViewCell:0x7fd536610300.height == 99   (active)>

And the image views' are not completely filling the whole cell, as seen in the UI hierarchy:

enter image description here


How do I create such a collection view, satisfying my requirements?

Upvotes: 1

Views: 475

Answers (1)

Sweeper
Sweeper

Reputation: 270810

I figured this out after a bit more looking around on the internet.

Apparently, there is this method called collectionView(_:layout:sizeForItemAt:) in CollectionViewDelegateFLowLayout. This is the first time that I know this method existed!

If I override that method, I can set the size of the cells however I want:

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

So the constraint I programmatically added isn't needed after all.

For the image view's frame problem, I solved it by trial and error. I unchecked this "relative to margin" option in the constraint's property inspector:

enter image description here

My guess is that the "margin" of the cell is 8 pixels, so setting it relative to the margin leaves out 8 pixels of space.

Upvotes: 1

Related Questions