Custom cells for UIPickerView with AutoLayout

For a project I needed to add a UIPickerView with custom cells using AutoLayout.  UIKit allows this via the UIPickerViewDelegate method pickerView(_:viewForRow:forComponent:reusingView).

The cell just needed an UIImageView and UILabel, so I thought it would be pretty straightforward to do, but there are some caveats you need to know. Big thanks to Tom Adriaenssen for pointing them out and not ruining my Sunday afternoon 🙂

The problem

This is what the problem looked like

UIPickedView with custom cells
UIPickedView with custom cells

I’ve added the following constraints:

  1. UILabel: center in superview (horizontal, vertical)
  2. UIImageView: Pin the Trailing edge to the Leading edge of the UILabel with an offset of -20
  3. UIImageView: center in superview (vertically)
  4. As the custom cell gets embedded into the UIPickerView I also pinned all 4 edges of my custom view to the superview with no insets
  5. I set thesetTranslatesAutoresizingMaskIntoConstraints method to false on my custom cell

The code looked like this

//
//  CustomPickerRowView.swift
//  CustomUIPickerCell
//
//  Created by Frederik Jacques on 05/07/15.
//  Copyright (c) 2015 Frederik Jacques. All rights reserved.
//

import UIKit
import PureLayout

class CustomPickerRowView: UIView {

    // MARK: - IBOutlets
    
    // MARK: - Properties
    let rowData:RowData
    
    var imageView:UIImageView!
    var label:UILabel!
    
    var didSetupConstraints:Bool = false
    
    // MARK: - Initializers methods
    init(frame: CGRect, rowData:RowData) {
        
        self.rowData = rowData
        
        super.init(frame: frame)
        
        backgroundColor = UIColor.purpleColor()
        
        self.setTranslatesAutoresizingMaskIntoConstraints(false)
        
        createImageView()
        createLabel()
        
    }

    required init(coder aDecoder: NSCoder) {
        
        fatalError("init(coder:) has not been implemented")
        
    }
    
    // MARK: - Lifecycle methods
    override func updateConstraints() {
        
        if( !didSetupConstraints ) {
            
            self.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsetsZero)
            
            label.autoCenterInSuperview()
            
            imageView.autoConstrainAttribute(.Horizontal, toAttribute: .Horizontal, ofView: self)
            imageView.autoPinEdge(.Trailing, toEdge: .Leading, ofView: label, withOffset: -10)
            
        }
        
        super.updateConstraints()
        
    }
    
    // MARK: - Private methods
    private func createImageView(){
    
        imageView = UIImageView.newAutoLayoutView()
        imageView.image = UIImage(named: rowData.imageName)
        addSubview(imageView)
        
    }
    
    private func createLabel(){
    
        label = UILabel.newAutoLayoutView()
        label.backgroundColor = UIColor.redColor()
        label.text = rowData.title
        addSubview(label)
        
    }
    
    // MARK: - Public methods
    
    // MARK: - Getter & setter methods
    
    // MARK: - IBActions
    
    // MARK: - Target-Action methods
    
    // MARK: - Notification handler methods
    
    // MARK: - Datasource methods
    
    // MARK: - Delegate methods

}

The solution

So doing the following resulted in the BAD! version of the image above. What did I do wrong?

First of all, you should _not_ set setTranslatesAutoresizingMaskIntoConstraints to false when you are using AutoLayout within a view which you don’t place (the UIPickerView will take care of this).

Second, don’t pin the edges of the custom cell to the superview! The UIPickerView will figure out the height.

And the most important part I forgot, you should call setNeedsUpdateConstraints() at the end of the init method!

You can check out the project on GitHub to see a working version.