×

How to Self-sizing cells with UITableView and Autolayout?


In the previous example we created default style of table view cell. Custom cells provide you the way to customize the cell in the way you want. Also, you will learn how to self-size or resize UITableView cells with UItableView and Autolayouts in this tutorial.

Task Output :-

In this task we create tableview which displays the programming languages and it's description. This example illustrates self-sizing of cells depending on the length of description and we will create our own custom tableView cell .


So Lets Start Creating UITableView .

Open Xcode Goto File > New >Project >Single View Application > Enter Project Name (eg :- FirstProjectViewController) and Select Language as Swift> Done.

Now you can see a file on left navigation Menu of Xcode named, ViewController.swift

We start first with AppDelegate.swift File. Copy and paste this code in didFinishLaunchingWithOptions method (the very first method you can see in Appdelegate.swift) .

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
              
        window = UIWindow(frame: UIScreen.main.bounds)
        let mainController = ViewController() as UIViewController
        let navigationController = UINavigationController(rootViewController: mainController)
        navigationController.navigationBar.isTranslucent = false
        self.window?.rootViewController = navigationController
        self.window?.makeKeyAndVisible()
        
        return true
    }

AppDelegate is the file which is called first when your App Starts . There are many ViewControllers added in your file . So How our App knows which one to start first ? So in didFinishLaunchingApplications we told our code to begin with ViewController file.

UINavigationController is mostly you see in every App. This is an header with teal color as you see in Final Output Image. Word itself tells its mean. It Controls all the navigation .

self.window?.rootViewController = navigationController

In this way you can set Root View Controller . In our case we want ViewController.swift file to be the first one to start.

ViewController.swift file looks like this :-
//  ViewController.swift
//  Created by Manish Methani.
//  Copyright © 2018 Codzify. All rights reserved.
//

import UIKit

private let myArray: NSMutableArray = ["C Language \nC is called as mother of programming languages used to develop System level Applications and originally developed by Denis M.Ritchie in 1970's to build Unix Operating System.",

"Objective-C \nThere are various ways to compile and run an Objective-C Program. But I recommend using to an Xcode (officially provided by Apple Inc.).All I can say about Xcode is It's amazing It includes everything you are seeing in iPhone. Each App you see in iPhones, iPad, and iWorld is developed in Xcode. All you have to know is just the basics of Objective-C Language to understand the syntax .",

"Swift \nSwift is a new programming language for iOS, macOS, watchOS, and tvOS app development. Many parts of Swift will be familiar with your C & Objective-C experience."]

var myTableView = UITableView()
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        self.view.backgroundColor = UIColor.white
        self.navigationItem.title = "Programming Languages"
        
        self.navigationController?.navigationBar.barTintColor = UIColor.init(red: 0/255.0, green: 128/255.0, blue: 128/255.0, alpha: 1.0)
        self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
        self.navigationController?.navigationBar.tintColor = UIColor.white
        
        
        myTableView = UITableView()
        myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
        myTableView.dataSource = self
        myTableView.delegate = self
        self.view.addSubview(myTableView)
        myTableView.backgroundColor = UIColor.init(red: 245, green: 245, blue: 245, alpha: 1)
        //Two Golden lines for resizing the table view cells
        myTableView.estimatedRowHeight = 100;
        myTableView.rowHeight = UITableViewAutomaticDimension;
        myTableView.separatorStyle = UITableViewCellSeparatorStyle.none;
        
       // Table View AutoLayout constraints
        myTableView.translatesAutoresizingMaskIntoConstraints = false
       
        let topConstraint = NSLayoutConstraint(item: myTableView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 12)
        self.view.addConstraint(topConstraint)
        
        let leftConstraint = NSLayoutConstraint(item: myTableView, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0)
        self.view.addConstraint(leftConstraint)
        
        let rightConstraint = NSLayoutConstraint(item: myTableView, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0)
        self.view.addConstraint(rightConstraint)
        
        let bottoConstraint = NSLayoutConstraint(item: myTableView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 0)
        self.view.addConstraint(bottoConstraint)
        
    }
    
 // Table View Delegates
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 0 // you should probably return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myArray.count
    }
    
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Num: \(indexPath.row)")
        print("Value: \(myArray[indexPath.row])")        
    }
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
        
        cell.backgroundColor = UIColor.clear
        cell.nameLabel.text  =  "\(myArray [indexPath.row])"
        
        return cell
    }
}

A short description of Example :-

We had given a backgroundColor to NavigationBar , Added a Programming Languages title and given a White text Color to Title .

Here, viewDidLoad() method is called first. In this method , we initialized a UITableView .

myTableView = UITableView()

Then , we registered a datasource and delegate methods of UITableView. We have to register these methods to implement its delegate methods.

 myTableView.dataSource = self
myTableView.delegate = self

Then you have to register the forCellReuseIdentifier and give it same name which you use in cellForRowAtIndexPath method of UITableView .

myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")

Last line adds myTableView as a subView inside view.

 self.view.addSubview(myTableView)

These lines helps in resizing the cells automatically. You do not have to give height to cells .

myTableView.estimatedRowHeight = 100;
myTableView.rowHeight = UITableViewAutomaticDimension;

In numberOfRowsInSection we are going to use the count of elements in our NSMutableArray to tell the tableView how many rows to create:

 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myArray.count
  }
 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
        
        cell.backgroundColor = UIColor.clear
        cell.nameLabel.text  =  "\(myArray [indexPath.row])"
        
        return cell
    }

CustomTableViewCell.swift file looks like this

We use Autolayout to create custom UITableView cell. If you missed the concepts of Autolayout you can refer it here.

import UIKit

class CustomTableViewCell: UITableViewCell {
    
    var nameLabel = UILabel()
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        self.selectionStyle = UITableViewCellSelectionStyle.none
       
      
        let boxView = UIView()
        self.contentView.backgroundColor = UIColor.clear
        boxView.backgroundColor = UIColor.white
        self.contentView.addSubview(boxView)
        boxView.layer.cornerRadius = 2.0;
        boxView.backgroundColor = UIColor.white;
        
        // BoxView created using autolayout constraints.
        boxView.translatesAutoresizingMaskIntoConstraints = false

        let topConstraint = NSLayoutConstraint(item: boxView, attribute: .top, relatedBy: .equal, toItem: self.contentView, attribute: .top, multiplier: 1, constant: 12)
        self.contentView.addConstraint(topConstraint)
        
        let leftConstraint = NSLayoutConstraint(item: boxView, attribute: .left, relatedBy: .equal, toItem: self.contentView, attribute: .left, multiplier: 1, constant: 12)
        self.contentView.addConstraint(leftConstraint)
        
        
        let rightConstraint = NSLayoutConstraint(item: boxView, attribute: .right, relatedBy: .equal, toItem: self.contentView, attribute: .right, multiplier: 1, constant: -12)
        self.contentView.addConstraint(rightConstraint)
        
        let bottoConstraint = NSLayoutConstraint(item: boxView, attribute: .bottom, relatedBy: .equal, toItem: self.contentView, attribute: .bottom, multiplier: 1, constant: -12)
        self.contentView.addConstraint(bottoConstraint)
        
        
        //Here label added to boxView
        nameLabel = UILabel()
        boxView.addSubview(nameLabel)
        nameLabel.textColor = UIColor.black
        nameLabel.numberOfLines = 0;
        nameLabel.lineBreakMode = NSLineBreakMode.byWordWrapping;

        // namelabel constraints 
        nameLabel.translatesAutoresizingMaskIntoConstraints = false

        let topLabelConstraint = NSLayoutConstraint(item: nameLabel, attribute: .top, relatedBy: .equal, toItem: boxView, attribute: .top, multiplier: 1, constant: 4)
        boxView.addConstraint(topLabelConstraint)
        
        let leftLabelConstraint = NSLayoutConstraint(item: nameLabel, attribute: .left, relatedBy: .equal, toItem: boxView, attribute: .left, multiplier: 1, constant: 12)
        boxView.addConstraint(leftLabelConstraint)
        
        
        let rightLabelConstraint = NSLayoutConstraint(item: nameLabel, attribute: .right, relatedBy: .equal, toItem: boxView, attribute: .right, multiplier: 1, constant: -12)
        boxView.addConstraint(rightLabelConstraint)
        
        
        //Separator line added to View  
        let separatorView = UIView()
        boxView.addSubview(separatorView)
        separatorView.backgroundColor = UIColor.lightGray
        
        // Separator line constraints
        separatorView.translatesAutoresizingMaskIntoConstraints = false
        
        let heightSeparatorConstraint = NSLayoutConstraint(item: separatorView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 2.0)
        boxView.addConstraint(heightSeparatorConstraint);
        
        let topSeparatorConstraint = NSLayoutConstraint(item: separatorView, attribute: .top, relatedBy: .equal, toItem: nameLabel, attribute: .bottom, multiplier: 1, constant: 12)
        boxView.addConstraint(topSeparatorConstraint)
        
        let leftSeparatorConstraint = NSLayoutConstraint(item: separatorView, attribute: .left, relatedBy: .equal, toItem: boxView, attribute: .left, multiplier: 1, constant: 12)
        boxView.addConstraint(leftSeparatorConstraint)
        
        
        let rightSeparatorConstraint = NSLayoutConstraint(item: separatorView, attribute: .right, relatedBy: .equal, toItem: boxView, attribute: .right, multiplier: 1, constant: -12)
        boxView.addConstraint(rightSeparatorConstraint)
        
      

        // Golden line which do all the resizing of cell stuff  
        let bottomSeparatorCosntraint = NSLayoutConstraint(item: separatorView, attribute: .bottom, relatedBy: .equal, toItem: boxView, attribute: .bottom, multiplier: 1, constant: -12)
        boxView.addConstraint(bottomSeparatorCosntraint)
        
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    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
    }
    
}

In this cell, we created one view boxView which is subview of self.contentView Then we added nameLabel which is subview of boxView. Now, we know that Size of nameLabel changes as per length of description of programming languages. So we need to resize the boxView accordingly.

// Golden line which do all the resizing of cell stuff  
        let bottomSeparatorCosntraint = NSLayoutConstraint(item: separatorView, attribute: .bottom, relatedBy: .equal, toItem: boxView, attribute: .bottom, multiplier: 1, constant: -12)
        boxView.addConstraint(bottomSeparatorCosntraint)
        

We added bottom of separatorView as bottom of boxView. top of separatorView will be bottom of nameLabel. top of nameLabel will be boxView relative and top of boxView will be top of self.contentView. Got it? :) Just read it like this to understand the heirarchy properly.