pheedsta
pheedsta

Reputation: 2080

UITableViewCell with indeterminate number of UIImageViews

I would like to create UITableViewCells that looks something like this:

enter image description here

As you can see, each cell is for a different friend and it displays a picture for each of their dogs (this is a fictitious example for demonstrative purposes only). Each person could have between zero and infinity number of dogs. I would like all information to be contained in one cell so that the user can touch that cell to transition the app to the next screen (notice how the second cell is selected). Each cell therefore needs some way of adding UIImageViews and UILabels as needed to accomodate the different number of dogs. Each cell also needs to return a cell height for the UITableView.

One thought was to use a UITableView within the cell, however, I understand that Apple frowns upon having a scroll view within another scroll view (not to mention the coding nightmare that would create). Should I consider the option of initialising each cell as needed (not dequeueing them), setting them with an array of images and dog names, then programatically add UIImageViews and UILabels to the cell (one for each dog)?

Upvotes: 1

Views: 53

Answers (2)

pheedsta
pheedsta

Reputation: 2080

Nicolas Miari's idea of using a UIStackView was spot on. Here is how I got it to work:

  1. Create a NIB called DogView.XIB with a UIImageView (imageView) and a UILabel (label). DogView.XIB

  2. Create a NIB called StackCell.XIB with a UITableViewCell and a UIStackView (stackView) added to the content view (Axis = Vertical; Alignment = Fill; Distribution = Equal Spacing; Spacing = 8). Every element of the cell (including the title label) will be added to the stackView.

  3. Register StackCell with the tableView and use the following code in your UITableViewDataSource:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        let stackCell = tableView.dequeueReusableCell(withIdentifier: "StackCell", for: indexPath) as! StackCell
    
        // need to remove any previous views added to UIStackView (cell may have been dequeued)
        for view in stackCell.stackView.arrangedSubviews {
            view.removeFromSuperview()
        }
    
        // create label for friend's name
        let friendLabel = UILabel()
        friendLabel.font = UIFont.boldSystemFont(ofSize: UIFont.labelFontSize)
        friendLabel.text = friendName
        stackCell.stackView.addArrangedSubview(friendLabel)
    
        // add dog views for each dog
        for dog in friendsDogArray {
            guard let dogView = Bundle.main.loadNibNamed("DogView", owner: nil, options: nil)?.first as? DogView else {
                continue
            }
    
            dogView.label.text = dog.name
            dogView.imageView.image = dog.image
            stackCell.stackView.addArrangedSubview(dogView)
        }
    
        return stackCell
    }
    

The great thing about this solution is the cell height is automatically calculated without any additional coding!

Upvotes: 1

Nicolas Miari
Nicolas Miari

Reputation: 16256

Nested table views wouldn't be such a coding nightmare if you house-keep your code correctly (agreed, you have two different contexts for "data source"...). And regarding event handling, clearly your specification calls for subtables that exactly span the whole row height so you can (should?) disable scrolling for all the inner the subtables.

But nested tables is certainly overkill. if you think that the inner cells aren't selectable or interactable in anyway, so...

Perhaps you can design a reusable/configurable "DogView", and have each cell of your (only, top-level) table contain:

  • The title label, and
  • A single UIStackView,

...to which you add a (variable) number of stacked subviews at runtime (of the type DogView mentioned above, of course), when you configure each cell.

You still need to make your cells variable height, but there's a lot of code and answers around to tell you how to achieve that.

Upvotes: 1

Related Questions