-1

I have 2 questions about constraints and auto layout:

  1. How programmatically create a cell-like in the picture below? I didn't understand how to programmatically add auto-layout and constraints for my cell. enter image description here
  2. How to assign default Apple layout margins to the cell? (for example, left inset in default cell equal 20 pt for big screens and 16 pt for small).

My current code:

class cellWithTitleAndDetail: UITableViewCell {
    
    // MARK: - Properties
    let title = UILabel()
    let detail = UILabel()
    let stackView = UIStackView()
    

    // MARK: - Override init
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        title.translatesAutoresizingMaskIntoConstraints = false
        detail.translatesAutoresizingMaskIntoConstraints = false
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .fillProportionally
        
        // Set color
        title.textColor = .white
        detail.textColor = .white
        
        // Highlight StackView
        stackView.addBackground(color: .blue)
        
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(detail)
        
        stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
        stackView.isLayoutMarginsRelativeArrangement = true

        self.contentView.addSubview(stackView)
                
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}

Result: enter image description here

Update:

Below added my new code and code from DonMag's answer.

New question: "LayoutMarginsGuide" works perfectly on iPhones with screen width that equal to 375 pt(image 375-1). But on big size screens separator appears earlier than the cell(image 414-2). How I can fix this?

enter image description here

New code:

// MARK: - Override init
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        // Off translatesAutoresizingMaskIntoConstraints
        title.translatesAutoresizingMaskIntoConstraints = false
        detail.translatesAutoresizingMaskIntoConstraints = false
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        // Setup stackView
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .fill
        
        // Hugging
        title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
        detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
        
        // Resistance
        title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
        detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
        
        // Set textAlignment
        title.textAlignment = .left
        detail.textAlignment = .right
        
        // Set numberOfLines
        title.numberOfLines = 0
        
        // Highlight stackView and set colors
        stackView.addBackground(color: .blue)
        title.textColor = .white
        detail.textColor = .white
        
        // Add title and detail
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(detail)
 
        // Add to subview
        self.contentView.addSubview(stackView)
        
        // Get layoutMarginsGuide
        let layoutMarginsGuide = contentView.layoutMarginsGuide
        
        // Set constraints
        NSLayoutConstraint.activate([
            
            // constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
            stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
            stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
            stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
            stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),

        ])
                
    }
Bandyliuk
  • 489
  • 4
  • 13

1 Answers1

1

You can use the Content View's layoutMarginsGuide:

    // only if you want different margins than the content view's margins
    //stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
    //stackView.isLayoutMarginsRelativeArrangement = true
    
    self.contentView.addSubview(stackView)
    
    let g = contentView.layoutMarginsGuide
    
    NSLayoutConstraint.activate([
        
        // constrain all 4 sides of the stack view to the
        //  content view's layoutMarginsGuide
        stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
        stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
        stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
        stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),

    ])
    

As a side note, it's not clear what you really want (and this would be a separate question if this doesn't provide the layout you want)...

  • Do you want your cells to look like "columns"
  • Do you want the "detail" to be right-aligned?
  • Might the detail label be multi-line?

Edit

Using your updated code - the only change I made is giving the labels a background color since you didn't show your stackView.addBackground(color: .blue) code:

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    // Off translatesAutoresizingMaskIntoConstraints
    title.translatesAutoresizingMaskIntoConstraints = false
    detail.translatesAutoresizingMaskIntoConstraints = false
    stackView.translatesAutoresizingMaskIntoConstraints = false
    
    // Setup stackView
    stackView.axis = .horizontal
    stackView.alignment = .center
    stackView.distribution = .fill
    
    // Hugging
    title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
    detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
    
    // Resistance
    title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
    detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
    
    // Set textAlignment
    title.textAlignment = .left
    detail.textAlignment = .right
    
    // Set numberOfLines
    title.numberOfLines = 0
    
    // Highlight stackView and set colors
    //stackView.addBackground(color: .blue)
    title.backgroundColor = .blue
    detail.backgroundColor = .red

    title.textColor = .white
    detail.textColor = .white
    
    // Add title and detail
    stackView.addArrangedSubview(title)
    stackView.addArrangedSubview(detail)

    // Add to subview
    self.contentView.addSubview(stackView)
    
    // Get layoutMarginsGuide
    let layoutMarginsGuide = contentView.layoutMarginsGuide
    
    // Set constraints
    NSLayoutConstraint.activate([
        
        // constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
        stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
        stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
        stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
        stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),

    ])
            
}

This is what I get:

enter image description hereenter image description here


Edit 2

Table view cell separators can change based on device, iOS version, table view style, etc.

For the most reliable consistency, set your own.

Here's an example...

  • we set the cell's stackView constraints relative to the contentView not to the content view's layout margins guide.
  • we set the table view's separatorInset so the left inset matches the stack view's leading anchor.
  • we also need to set each cell's .separatorInset equal to our table view's custom .separatorInset

Here's the full code:

class MyTestTableViewController: UITableViewController {
    
    let testTitles: [String] = [
        "Yesterday all my troubles seemed so far away, Now it looks as though they're here to stay.",
        "She packed my bags last night pre-flight, Zero hour nine AM.",
        "When you're weary, feeling small, When tears are in your eyes, I will dry them all."
    ]
    let testDetails: [String] = [
        "The Beatles",
        "Elton John",
        "Simon & Garfunkel",
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.register(MyCellWithTitleAndDetail.self, forCellReuseIdentifier: "myCell")
        
        // our custom separatorInset
        //  left matches cell's stackView leading anchor
        tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCellWithTitleAndDetail
            
        // set cell separatorInset equal to tableView separatorInset
        cell.separatorInset = tableView.separatorInset
            
        cell.title.text = testTitles[indexPath.row]
        cell.detail.text = testDetails[indexPath.row]
        
        return cell

    }

}

class MyCellWithTitleAndDetail: UITableViewCell {
    
    // MARK: - Properties
    let title = UILabel()
    let detail = UILabel()
    let stackView = UIStackView()
    
    
    // MARK: - Override init
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        // Off translatesAutoresizingMaskIntoConstraints
        title.translatesAutoresizingMaskIntoConstraints = false
        detail.translatesAutoresizingMaskIntoConstraints = false
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        // Setup stackView
        stackView.axis = .horizontal
        stackView.alignment = .center
        
        stackView.distribution = .fillEqually

        // if we want the labels to be 50% of the width,
        //  set stackView.distribution = .fillEqually
        //  then we don't need any Content Hugging or Compression Resistance priority changes
        
//      // Hugging
//      title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
//      detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
//
//      // Resistance
//      title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
//      detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)

        // Set textAlignment
        title.textAlignment = .left
        detail.textAlignment = .right
        
        // Set numberOfLines
        title.numberOfLines = 0
        
        // Highlight stackView and set colors
        
        title.backgroundColor = .blue
        detail.backgroundColor = .red
        //stackView.addBackground(color: .blue)
        
        title.textColor = .white
        detail.textColor = .white
        
        // Add title and detail
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(detail)
        
        // Add to subview
        self.contentView.addSubview(stackView)
        
        // Set constraints
        NSLayoutConstraint.activate([
            
            // constrain all 4 sides of the stack view to the content view
            //  with your own "margins"
            stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
            stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0),
            stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15.0),
            stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
            
        ])
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

and the results:

enter image description here

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • @Bandyliuk - It's not helpful to get an answer to your question, and then edit your question with new questions. If you get an answer that helps, accept it... if you have **new** questions, make a new post. That said, see the edit to my answer. There must be something else you are doing that is causing the misalignment with the separator. – DonMag Jul 06 '20 at 19:23
  • sorry, that I at first didn't click "accept the answer". Thanks for your help. – Bandyliuk Jul 06 '20 at 19:40
  • Can you change table style to "UITableView.Style.insetGrouped" and try run code again? Because with "plain" style my code works well, but with "insetGrouped" – no. – Bandyliuk Jul 06 '20 at 19:47
  • @Bandyliuk - for future reference: when you post here on Stack Overflow, try to include **all** information. See the **Edit 2** at the bottom of my answer. – DonMag Jul 07 '20 at 15:21