XAMPPRocky
XAMPPRocky

Reputation: 3619

Swift TableViewCell xib doesn't have constraints

I have two TableViews, when I designed the original I designed it using the prototype cell in the storyboard, to make it reusable I tried to pull it out into a .xib and load that instead. When it is loaded from cellID.xib however it loses all the constraints at runtime, and everything is layered on top of each other.

TableViewController

let cellIdentifier = "cellID"
override func viewDidLoad() {
    super.viewDidLoad()

    tableView.register(UINib(nibName: cellIdentifier, bundle: nil), forCellReuseIdentifier: cellIdentifier)
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 300
}

Prototype cell in Storyboard (Used to work)

prototype cell

Cell when copy pasted to XIB

xib cell

Constraints

contraints on cell

XIB View Hierarchy

xib view hierarchy

Upvotes: 3

Views: 4369

Answers (4)

Anas
Anas

Reputation: 293

Solution :

I was achive solution to your problem. hope it might be help to you

I use uiview to clips all cell elements.enter image description here

And Cell Design enter image description here

Code :

class ViewController: UIViewController {

@IBOutlet var tableView: UITableView!



 override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.estimatedRowHeight = 100
        self.tableView.rowHeight = UITableViewAutomaticDimension
        self.tableView.register(UINib(nibName: "TestCell", bundle: Bundle.main), forCellReuseIdentifier: "TestCell")

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

extension ViewController : UITableViewDataSource, UITableViewDelegate {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1;
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestCell

        cell.selectionStyle = .none

        return cell
    }

}

Result : enter image description here

Upvotes: 0

Dhiru
Dhiru

Reputation: 3060

Problem and Solution

You may have disabled the Autolayout for your XIB to turn it ON go to File Inspector from Top Right Menu

enter image description here

You Can Follow the Simple Steps to Create Cell from Xib 😊

1. Create UITableCell with .xib ( ✅ also create Xib file) enter image description here

2. Give the cell identifier

enter image description here

3. Layout Constraints

🔸 ImageView give contarints from Top , Left and Bottom height constant and Aspect ratio [i have set Border to show the frame]

🔸 UIlable for the title lable give constraints from three sides from Top , Left and Right NumberOflines=0 (🚫 Don't give height constant - ⚠️ if gives warning go ahead and update the constraints)

🔸 UIlable for the Descriptions lable give constraints from all four sides also NumberOflines=0 (⚠️ This will give you an error go to suggestion and change the priority of UIlable constraints)

see the screenshot below enter image description here

changing priority enter image description here than again update contraints

4. Code in UIViewController

 @IBOutlet var tableView: UITableView!

    var cellIdentifier="cell"
    var cellXibName = "MyTableCell"


    override func viewDidLoad() {
        super.viewDidLoad()


        tableView.register(UINib(nibName: cellXibName, bundle: nil), forCellReuseIdentifier: cellIdentifier)
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 320


    }



    func numberOfSections(in tableView1: UITableView) -> Int {
        return 3
    }

     func tableView(_ tableView1: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cell :MyTableCell = tableView1.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableCell


            return cell
    }

And Finally the Output is perfect as you want

OUTPUTenter image description here

I hope this will work for you...... please let me know if this worked for you or not.

Upvotes: 2

Zion
Zion

Reputation: 1566

The Problem

In your question title, you asked how to solve an issue where "Swift TableViewCell xib doesn't have constraints." Additionally in the bounty, you specify that the answer should be:

An adequate answer showing why constraints are lost when moved to a xib, when they exist in the xib, and how to address this problem.

From my understanding, your question has two parts:

  1. Issue where a custom TableViewCell xib does not have constraints
  2. Why constraints are lost when copy/pasted from Storyboard prototype to a xib

For issue #1, I used almost all of the same constraints as you had displayed in your screenshot in a custom cell xib and it worked. I will explain in more detail below.

For issue #2, I found that constraints are not lost. You may need to share your project so that others can replicate the issue you are having. I copy/pasted a Storyboard prototype cell with similar constraints over to a xib and I did not have any issues with constraints. So I can confirm that the copy/paste feature does work.

Solution Demo

I created the following demo to test your issue. Refer to the screenshot below. The tableView contains six cells. The 1st and 2nd are the same and have reuse identifier MyCell. The 3rd and 4th are the same and have reuse identifier MyCopyPasteCell. The 5th and 6th are the same and have reuse identifier MyPrototypeCell.

MyCell exists in a xib and uses nearly all the same constraints that you showed in your screenshot. As you can see, there are no constraints issues. MyCopyPasteCell uses similar constraints and was copy/pasted from a Storyboard prototype over to a xib. MyPrototypeCell is the original prototype that was copied. These last four cells look exactly the same even though the prototype was copied over to the xib. The constraints carried over from prototype to xib without an issue.

enter image description here

Constraints

In the code below, I list all the constraints that you showed in your screenshot for your ContentView. (Note that I also implemented the height=20, aspect=1:1, and height=75 constraints, although they are not listed below.) Two constraints are commented out since I did not use them. I also added one more constraint to replace another unused one.

// bottomMargin = Profile Image View.bottom + 188.5
bottomMargin = Profile Image.bottom + 17
Profile Image View.top = Username Label.top
Profile Image View.leading = leadingMargin + 2
Profile Image View.top = topMargin + 20
Username Label.leading = Content Text View.leading    
// Username Label.top = topMargin + 20        
Username Label.trailing = Content Text View.trailing
Username Label.leading = Profile Image View.trailing + 15
trailingMargin = Username Label.trailing + 10
Content Text View.top = Username Label.bottom + 5
Content Text View.leading = leadingMargin + 92
bottomMargin = Content Text View.bottom + 20

The first commented constraint // bottomMargin = Profile Image View.bottom + 188.5 did not make sense as it would separate the bottom of the image from the bottom of the cell by 188.5. This also did not match your Prototype cell in Storyboard (Used to work) screenshot at all. I replaced it with bottomMargin = Profile Image.bottom + 17, which just replaces the 188.5 with 17.

The second commented constraint // Username Label.top = topMargin + 20 (which separates username and topMargin by 20) can technically work. However, its functionality is redundant to Profile Image.top = topMargin + 20 (which separates Profile Image and topMargin by 20) and Profile Image View.top = Username Label.top (which sets the Profile Image and Username to the same top separation i.e. by 20).

MyTableViewController

The following is my view controller. Basically I create three sections with two cells/rows per section. MyCell and MyCopyPasteTableViewCell are from a xib and are registered in the viewDidLoad(). MyPrototypeCell is from the Storyboard and is not registered in viewDidLoad().

//  MyTableViewController.swift
import UIKit    
class MyTableViewController: UITableViewController {

    let myCell = "MyCell"
    let myCopyPasteCell = "MyCopyPasteTableViewCell"
    let myPrototypeCell = "MyPrototypeCell"

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UINib(nibName: myCell, bundle: nil), forCellReuseIdentifier: myCell)
        tableView.register(UINib(nibName: myCopyPasteCell, bundle: nil), forCellReuseIdentifier: myCopyPasteCell)
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 300
    }

    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: myCell, for: indexPath)
            return cell
        } else if indexPath.section == 1 {
            let cell = tableView.dequeueReusableCell(withIdentifier: myCopyPasteCell, for: indexPath)
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: myPrototypeCell, for: indexPath)
            return cell
        }
    }
}

Github link

https://github.com/starkindustries/TableViewCellConstraintsTest

Upvotes: 6

mcd
mcd

Reputation: 726

It seems that the cell height is the problem, right? Because of that the constraints can't do their work correctly.

You need this override func when you work with XIB files:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return 300
}

However your constraints must be wrong or let us say they are not perfect. I know they are working with the prototype cells in storyboard but that don't make them 100% perfect working right? Your UIImageView has no height and width. You should try to set that with some constraints. (or you set the width and use the Aspect Ratio constraint)

Upvotes: -1

Related Questions