swiftuitableviewuitableviewautomaticdimension

Multiple UITableViewCell problem in displaying


I'm new in autolayout programmatically, and I have a problem with displaying 2 different cells in UITableView as below:

enter image description here

The first cell I want to show profiles picture and the username, then the menu options. But as screenshot the second section menu option is displaying behind the first section (user profile picture and username).

How to solve this problem?

UserTableViewCell:

class UserTableViewCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        backgroundColor = .CustomGreen()
        selectionStyle = .none
    }

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

    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .clear
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    }()

    lazy var userPhoto: UIImageView = {
        let userPhoto = UIImageView()
        userPhoto.translatesAutoresizingMaskIntoConstraints = false
        userPhoto.layer.cornerRadius = userPhoto.frame.size.width / 2
        userPhoto.clipsToBounds = true
        return userPhoto
    }()

    lazy var username: UILabel = {
        let username = UILabel()
        username.textColor = .white
        username.text = "Ahmed Abd Elaziz"
        username.translatesAutoresizingMaskIntoConstraints = false
        return username
    }()

    func setupContainerViewConstraints() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor),
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    }

    func setupUserPhotoConstraints() {
        NSLayoutConstraint.activate([
            userPhoto.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
            userPhoto.widthAnchor.constraint(equalToConstant: frame.width / 3),
            userPhoto.heightAnchor.constraint(equalToConstant: frame.width / 3)
        ])
    }

    func setupUsernameConstraints() {
        NSLayoutConstraint.activate([
            username.topAnchor.constraint(lessThanOrEqualTo: userPhoto.bottomAnchor, constant: 16),
            username.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
        ])
    }

    func addSubviews() {
        addSubview(containerView)
        containerView.addSubview(userPhoto)
        containerView.addSubview(username)
    }

    func layoutUI() {
        addSubviews()
        setupContainerViewConstraints()
        setupUserPhotoConstraints()
        setupUsernameConstraints()
    }

}

SideMenuOptionTableViewCell:

class SideMenuOptionTableViewCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        backgroundColor = .CustomGreen()
        selectionStyle = .none
    }

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

    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .clear
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    }()

    lazy var viewTitle: UILabel = {
        let viewTitle = UILabel()
        viewTitle.font = UIFont(name: "AvenirNext-Regular", size: 20)
        viewTitle.textColor = .white
        viewTitle.numberOfLines = 0
        viewTitle.translatesAutoresizingMaskIntoConstraints = false
        return viewTitle
    }()

    func setupContainerViewConstraints() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor),
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    }

    func setupFoodTitle() {
        NSLayoutConstraint.activate([
            viewTitle.centerYAnchor.constraint(equalTo: containerView.centerYAnchor),
            viewTitle.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16)
        ])
    }

    func addSubview() {
        addSubview(containerView)
        containerView.addSubview(viewTitle)
    }

    func layoutUI() {
        addSubview()
        setupContainerViewConstraints()
        setupFoodTitle()
    }

}

SideMenuTableViewController:

class SideMenuTableViewController: UITableViewController {

    let viewControllers = ["Controller One", "Logout"]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UserTableViewCell.self, forCellReuseIdentifier: "UserTableViewCell")
        tableView.register(SideMenuOptionTableViewCell.self, forCellReuseIdentifier: "SideMenuOptionTableViewCell")
        tableView.reloadData()
        tableView.backgroundColor = .CustomGreen()
        tableView.separatorStyle = .none
        tableView.rowHeight = UITableView.automaticDimension
//        tableView.estimatedRowHeight = 100
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return section == 0 ? 1 : viewControllers.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
            cell.userPhoto.image = UIImage(named: "ahmed")
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "SideMenuOptionTableViewCell", for: indexPath) as! SideMenuOptionTableViewCell
            cell.viewTitle.text = viewControllers[indexPath.row]
            return cell
        }

    }

}

Solution

  • The problem here is you are adding constraints according to the cells height and all the views are being adjusted in the cell. As the space for the views is too small they are overlapping the views of the next cell. You can fix this problem by increasing the size of the content view of the cell.

    Add this line in init of your UserTableViewCell

    let minHeight = 70
    let minHeightConstraint = contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
    minHeightConstraint.priority = UILayoutPriority(rawValue: 999)
    minHeightConstraint.isActive = true