3

Something seemingly simple turned out to be really hard. I have browsed already multiple topics here and on SnapKit GitHub but failed to solve my issue.

I want to have UITableViewCell with a label that is positioned in the middle let's say both 50 from top and bottom the cell.

It's worth to mention that the cell is created programatically

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    self.addSubview(titleLabel)
    titleLabel.snp.makeConstraints { (make) -> Void in
        make.topMargin.equalToSuperview().offset(50.0)
        make.left.equalToSuperview().inset(UIView.getValueScaledByScreenWidthFor(baseValue:10.0))
        make.bottomMargin.equalToSuperview().offset(50.0)

    }
}

In the ViewController I tried both approaches for automatic cell height:

 extension EpisodeViewController: UITableViewDelegate {
  func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
  }
}

And

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension

in viewDidLoad() method

What I get is:

enter image description here

Each of these three cells should be populated with "Test" - instead it has pushed down the label below the respective cell without resizing the cell.

Tried many various combinations such as:

1) Setting contraint priority to 999 - no change

2) Adding to contentView instead of self - does not show at all

3) Using top instead of topMargin etc - makes no difference

Could you advice me what's wrong with this code, and what is the general rule of thumb while using SnapKit in programatically created cells that should autoresize its height based on constraint?

Thanks in advance

EDIT

UITableView DataSource methods

extension EpisodeViewController: UITableViewDataSource {

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: EpisodeHeaderCell = tableView.dequeueReusableCell(for: indexPath)
    cell.viewModel = viewModel
    return cell
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 3
}
func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

}
Bhavesh Nayi
  • 705
  • 4
  • 15
theDC
  • 6,364
  • 10
  • 56
  • 98

2 Answers2

5
    label.snp.makeConstraints {
        $0.left.equalToSuperview().offset(10)
        $0.right.equalToSuperview().offset(-10)
        $0.top.equalToSuperview().offset(50)
        $0.bottom.equalToSuperview().offset(-50)
    }

Here is the whole code of viewController.swift file.

class TestCell: UITableViewCell {
    static let identifier: String = "test_cell_identifier"

    var label: UILabel!

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.configure()
    }

    func configure() {
        label = UILabel(frame: .zero)
        self.contentView.addSubview(label)
        label.snp.makeConstraints {
            $0.left.equalToSuperview().offset(10)
            $0.right.equalToSuperview().offset(-10)
            $0.top.equalToSuperview().offset(50)
            $0.bottom.equalToSuperview().offset(-50)
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class ViewController: UIViewController {
    var data: [String] = [
        "Test1",
        "Test2",
        "Test3"
    ]

    var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView = UITableView(frame: .zero)
        self.view.addSubview(tableView)
        tableView.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }
        tableView.register(TestCell.self, forCellReuseIdentifier: TestCell.identifier)
        tableView.dataSource = self
        tableView.delegate = self

        tableView.estimatedRowHeight = 100
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: TestCell.identifier, for: indexPath) as! TestCell
        cell.label.text = data[indexPath.item]
        return cell
    }
}
Li Jin
  • 1,879
  • 2
  • 16
  • 23
  • Thank you, but I don't think it's related to ViewModel nor I added it via storyboard which is explained in question title. – theDC Apr 30 '19 at 13:00
  • did you try this? cell.layoutIfNeeded() I answered with all possible cases to help you solve it. – Li Jin Apr 30 '19 at 13:02
  • cell.layoutIfNeeded() should work. why did you give a `minus` to my answer? – Li Jin Apr 30 '19 at 13:08
  • It does not unfortunately, I can cancel this downvote but please edit your answer to match the question (no storyboards) – theDC Apr 30 '19 at 13:16
  • downvote is not important to me. i am really curious why it doesn't work for you. if you want, i will send you a complete sample that contains 1 label and works really fine. – Li Jin Apr 30 '19 at 13:18
  • please share me the code, i can fix it within a min. – Li Jin Apr 30 '19 at 13:19
  • Updated the answer, try and let me know if it works for you. – Li Jin Apr 30 '19 at 13:24
  • unfortunately it does not – theDC Apr 30 '19 at 13:47
  • Updated the answer, please test it. – Li Jin Apr 30 '19 at 14:02
  • 1
    @LiJin - your code works, but couple issues... 1) `self.addSubview(label)` should be `contentView.addSubview(label)` ... 2) `tableView.rowHeight = UITableView.automaticDimension` is the default, so not needed ... 3) `tableView.estimatedRowHeight` should not be `UITableView.automaticDimension` - it should be an estimated value, in this case, 120 would be reasonable ... 4) Don't call `cell.layoutIfNeeded()` – DonMag Apr 30 '19 at 14:27
  • It works, I confirm `layoutIfNeeded()` is not needed! – theDC Apr 30 '19 at 14:34
1

UITableViewCell's have contentView which should be used for adding custom views

So try something like this (I havn't tested it)

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    self.contentView.addSubview(titleLabel)

    titleLabel.snp.makeConstraints { (make) -> Void in
        // Changed from bottom to top
        make.top.equalTo(self.contentView.snp.top).offset(50)
        make.bottom.equalTo(self.contentView.snp.bottom).offset(50)
        make.left.equalTo(self.contentView.snp.left)
        make.right.equalTo(self.contentView.snp.right)
    }
}
Cerlin
  • 6,622
  • 1
  • 20
  • 28
  • @DCDC Sorry there is a mistake in the code. Most likely it should work now. I have added comments on what i have changed – Cerlin Apr 30 '19 at 12:31
  • Ok, gotcha, but now it looks like pretty much the same as in the picture in original post - the label goes beyond the cell, which remains unchanged – theDC Apr 30 '19 at 12:36