0
votes

I have followed this tutorial to create a custom .xib, which I plan to use in a table view's cell:

https://medium.com/@brianclouser/swift-3-creating-a-custom-view-from-a-xib-ecdfe5b3a960

Here is the .xib's class I created:

class UserView: UIView {

    @IBOutlet var view: UIView!
    @IBOutlet weak var username: UILabel!

    override init(frame: CGRect) {
        super.init(frame: frame)
        initialize()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initialize()
    }

    private func initialize() {
        Bundle.main.loadNibNamed("UserView", owner: self, options: nil)
        addSubview(view)
        view.frame = self.bounds
        view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    }

}

Previously, I was creating my table view cell within the storyboard, but I've come to realize that I want a more flexible view so that I can use it in different parts of my app, so I created the above custom .xib, UserView.

I have updated the table view cell in the storyboard to use the custom .xib:

https://i.stack.imgur.com/t7Tr7.png

Here is what my table view controller class looked like prior to creating the custom .xib (i.e. making the layout in the storyboard):

class UserTableViewController: UITableViewController {

    // MARK: Properties

    let provider = MoyaProvider<ApiService>()
    var users = [User]()

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.estimatedRowHeight = 100
        tableView.rowHeight = UITableViewAutomaticDimension

        // Fetch the user by their username
        provider.request(.getUsers()) { result in
            switch result {
            case let .success(response):
                do {
                    let results = try JSONDecoder().decode(Pagination<[User]>.self, from: response.data)

                    self.users.append(contentsOf: results.data)

                    self.tableView.reloadData()
                } catch {
                    print(error)
                }
            case let .failure(error):
                print(error)
                break
            }
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

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

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

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

        let cellIdentifier = "UserTableViewCell"

        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? UserTableViewCell  else {
            fatalError("The dequeued cell is not an instance of UserTableViewCell.")
        }

        let user = users[indexPath.row]

        cell.username.text = user.username

        return cell
    }

}

Here is the table view cell class:

class UserTableViewCell: UITableViewCell {

    //MARK: Properties

    @IBOutlet weak var userView: UserView!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

My question is, how do I update the above table view controller class to use my custom .xib, instead of using the storyboard layout?

1
Where is the code for UserTableViewCell ?Dinsen
@Dinsen Added the class.user9815123
So you have a custom xib for both UserTableViewCell and UserView?Dinsen
No, just a custom xib for UserView, which is a UIView.user9815123

1 Answers

0
votes

You can use 2 ways:

Create UITableViewCell (better)

1) Change UIView to UITableViewCell

class CustomTableViewCell: UITableViewCell { 

    ...

    class var identifier: String {
        return String(describing: self)
    }
}

2) Register your cell

override func viewDidLoad() {
    super.viewDidLoad()

    self.tableView.registerNib(UINib(nibName: CustomTableViewCell.identifier, bundle: nil), forCellReuseIdentifier: CustomTableViewCell.identifier)
    ...
}

3) Use cellForRow(at:)

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

    let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.identifier) as! CustomTableViewCell
    cell.username.text = user.username

    return cell
}

OR Add view as subview to cell (only in rare cases)

1) Add this to UserView

class UserView: UIView {

    ...

    class func fromNib() -> UserView {
        return UINib(nibName: String(describing: self), bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UserView
    }

}

2) Use cellForRow(at:)

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

    let cellIdentifier = "UserTableViewCell"

    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? UserTableViewCell  else {
        fatalError("The dequeued cell is not an instance of UserTableViewCell.")
    }

    let userView = UserView.fromNib()
    let user = users[indexPath.row]
    userView.username.text = user.username

    //Use frame size, but for me better to add 4 constraints
    userView.frame = CGRect(x: 0, y: 0, width: cellWidth, height: cellHeight)

    cell.contentView.addSubview(UserView)

    return cell
}