1

I'm trying to make a custom tableView cell that has a stackView from an array images.

I used interface builder and added a horizontal stackView with: Center X Center Y Top +12 Bottom +12

I'm using a loop to add Arranged Subview and it works fine. But the images are super small, even though they shouldn't be due to their CGRect. But they are getting adjusted by the stackView.

The idea was to keep the stack centered, while also using the top/bottom to adjust the hight of the cell.

I've reasoned that the issue I'm facing with the images being small is because the stackView is initialized empty. So the stackViews hight is very tiny. And because imageViews are being added inside of it, they are then being resized to fit.

How can you get a cell/stackView to adjust their hight after being displayed?

Note: I've also been having issues with trying to add a top & bottom constraint to the imageView programmatically.

for pictogram in pictograms {
            let imageView = UIImageView()
            
            let size = self.bounds.width / CGFloat(pictograms.count + 1)
            print("size: ", size)
            
            imageView.frame = CGRect(x: 0, y: 0, width: size, height: size)
            imageView.contentMode = .scaleAspectFit
            
            imageView.image = pictogram.image
            pictogramStack.addArrangedSubview(imageView)
        }
StonedStudio
  • 507
  • 3
  • 20
  • There is no problem adding Arranged Subviews to a stack view that starts empty. Something else you are doing with your constraints is causing the issue. You need to show an image of your cell prototype (make sure it shows all your constraints), and include the code you're using to add your arranged subviews. – DonMag Jun 29 '20 at 13:13
  • Hey Don, there's no problem with adding the views into the stack. The issue is is setting the hight of the cell. - The cell only contains the stack view, with the 4 constraints mentioned. – StonedStudio Jun 29 '20 at 16:16
  • If you instantiate a `UIImageView`, give its `.frame` a `CGRect`, and then add it as an `arrangedSubview` of a stack view, that `CGRect` will be ignored. That's why I suggested you show the code you're using to add your arranged subviews. – DonMag Jun 29 '20 at 17:16
  • Hey Don, Thats how I'm doing it. - Code added. – StonedStudio Jun 29 '20 at 17:41
  • OK - a little more clarification... are you adding imageViews to a *horizontal* stack view? And you want them equal widths? And you want heights = widths (1:1 ratio)? – DonMag Jun 29 '20 at 17:53
  • Hey Don, Apologies. Yes, the images should be the same size. with a 1:1 ratio. And yes it is a horizontal stack. – StonedStudio Jun 29 '20 at 18:23

1 Answers1

1

You are using a UIStackView in the wrong way. Stack views will arrange the subviews for you - no need to be calculating widths and setting frames.

Layout your cell prototype like this:

enter image description here

Note that the Bottom constraint has Priority: 999. Auto-layout needs to make multiple "passes" to lay out stack views (particularly in table view cells). Using a priority of 999 for the bottom constraint will avoid Constraint Conflict error / warning messages.

Set the stack view properties like this:

enter image description here

With Distribution: Fill Equally we don't have to do any let size = self.bounds.width / CGFloat(pictograms.count + 1) kind of calculations.

Also, to make design-time a little easier, give the stack view a Placeholder intrinsic height:

enter image description here

That will have no effect at run-time, but allows you to clearly see your cell elements at design time.

Now, when you "fill" the stack view with image views, no .frame = setting, and the only constraint you need to add is Height == Width:

NSLayoutConstraint.activate([
    // image view should have 1:1 ratio
    imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
])

Here is a complete example:

class HorizontalImagesStackCell: UITableViewCell {
    
    @IBOutlet var theStack: UIStackView!
    
    func addImages(_ pictograms: [String]) -> Void {
        
        // cells are reused, so remove any previously added image views
        theStack.arrangedSubviews.forEach {
            $0.removeFromSuperview()
        }
        
        pictograms.forEach { s in
            // make sure we can load the image
            if let img = UIImage(named: s) {
                // instantiate an image view
                let imageView = UIImageView()
                // give it a background color so we can see its frame
                imageView.backgroundColor = .green
                // scale aspect fit
                imageView.contentMode = .scaleAspectFit
                // set the image
                imageView.image = img
                NSLayoutConstraint.activate([
                    // image view should have 1:1 ratio
                    imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
                ])
                // add it to the stack
                theStack.addArrangedSubview(imageView)
            }
        }
        
    }
}

class ImagesInStackTableViewController: UITableViewController {
    
    // we'll display 5 rows of images
    //  going from 5 images to 4 images ... to 1 image
    let myData: [Int] = [5, 4, 3, 2, 1]
    
    let myImages: [String] = [
        "img1", "img2", "img3", "img4", "img5"
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myData.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "HCell", for: indexPath) as! HorizontalImagesStackCell

        // get the first n number of images
        let a: [String] = Array(myImages.prefix(myData[indexPath.row]))
        
        cell.addImages(a)
        
        return cell
    }
}

Using these 5 images:

enter image description hereenter image description hereenter image description hereenter image description hereenter image description here

We get this result (image views are set to .scaleAspectFit and have green backgrounds so we can see the frames):

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Hey Don, Thanks for writing this up. It's a great guide. - Ive followed it though and I'm still having issues getting the cell to load at a size greater than the standard cell hight. (Regardless if the height is set to automatic or not.) – StonedStudio Jun 29 '20 at 22:40
  • Update: Your loop didn't work for me. For some reason it was adding images in twice. And still at the wrong size. Went back to the loop I originally was using, and removed the intrinsic size, and added back in setting the frame and finally, (Major props for bottom @999) its working! :) Also - theNSLayoutConstraint! – StonedStudio Jun 29 '20 at 22:49
  • Update 2: It really looks like it's just the NSLayoutConstraint thats the game changer. Along with the @999 – StonedStudio Jun 29 '20 at 22:52