×

iOS UITableView's Custom Cell


What is UITableView's Custom Cell ?

UITableView Custom Cell is used to customise the content of a tableView cell (row). You can add profileImage , Name , description or anything you want in your own way. But before that you should know how to add label , Image , buttons , UIViews etc .

Example

In this example we are going to create an Contacts List App like the one in WhatsApp.

Fnal Output :-


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

Now you see two files on left navigation Menu of Xcode , ViewController.h and ViewController.m

Now start runnning a program by clicking black forward Button on top of XCode.


You will see blank white screen because we haven't written any code

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

#import "AppDelegate.h"
#import "ViewController.h"   /* Import this because this is aview Controller which we want to start first. */

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    ViewController *abcController = [[ViewController alloc]init];
    
    UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:abcController];
     self.window.rootViewController = navController;
    [self.window makeKeyAndVisible];
    return YES;
}

@end

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 blue color as you see in Final Output Image. Word itself tells its mean. It Controls all the navigation .

self.window.rootViewController = navController;

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

In ViewController.m > viewDidLoad() method

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view
    
    /* Change backgroundColor of Navigation Bar */
    NSArray *ver = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."];
    if ([[ver objectAtIndex:0] intValue] >= 7) {
        // iOS 7.0 or later
        self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:0.0/255.0 green:120.0/255.0 blue:176.0/255.0 alpha:1.0];
        self.navigationController.navigationBar.translucent = NO;
    }else {
        // iOS 6.1 or earlier
        self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:0.0/255.0 green:120.0/255.0 blue:176.0/255.0 alpha:1.0];
    }
    
    /* give title to navigation bar */ 
    self.navigationItem.title = @"Contacts";
    
    /* Change the textColor of navigation bar title */ 
    [self.navigationController.navigationBar setTitleTextAttributes:
     @{NSForegroundColorAttributeName:[UIColor whiteColor]}];
    }

Comments itself are self explanatory. What we did til now is we had given a backgroundColor to NavigationBar , Added a Contacts title and given a White text Color to Title .

Now we alloc a UITableView . Add this code in viewDidLoad method .

UITableView *tableViewContacts = [[UITableView alloc]initWithFrame:CGRectMake(0, 12, self.view.frame.size.width, self.view.frame.size.height)];
    [self.view addSubview:tableViewContacts];
    tableViewContacts.delegate = self;
    tableViewContacts.dataSource = self;

I had explained basics of UITable View Here. How to allocate it and whats dataSource and delegates are.

It's time to add Some Contacts in an array which you want to display in Contacts App

Add this in viewDidLoad method itself. This is the place where all the initialisation process takes oplace.

 contactsArray = [[NSMutableArray alloc]init];
    
    NSMutableDictionary *jasonDict = [[NSMutableDictionary alloc]init];
    [jasonDict setObject:@"Jason" forKey:@"name"];
    [jasonDict setObject:@"9824xxxxxx" forKey:@"mobileNumber"];
    
    [contactsArray addObject:jasonDict]; 
    
    
    NSMutableDictionary *stevenDict = [[NSMutableDictionary alloc]init];
    [stevenDict setObject:@"Steven" forKey:@"name"];
    [stevenDict setObject:@"9824xxxxxx" forKey:@"mobileNumber"];
    
    [contactsArray addObject:stevenDict]; 
    
    
    NSMutableDictionary *jazzyDict = [[NSMutableDictionary alloc]init];
    [jazzyDict setObject:@"Jazzy" forKey:@"name"];
    [jazzyDict setObject:@"9824xxxxxx" forKey:@"mobileNumber"];
    
    [contactsArray addObject:jazzyDict]; 
    

    NSMutableDictionary *lawrenDict = [[NSMutableDictionary alloc]init];
    [lawrenDict setObject:@"Lawren" forKey:@"name"];
    [lawrenDict setObject:@"9824xxxxxx" forKey:@"mobileNumber"];
    
    [contactsArray addObject:lawrenDict]; 
    
    
    NSMutableDictionary *suzaenDict = [[NSMutableDictionary alloc]init];
    [suzaenDict setObject:@"Suzaen" forKey:@"name"];
    [suzaenDict setObject:@"9824xxxxxx" forKey:@"mobileNumber"];
    
    [contactsArray addObject:suzaenDict];
  

What we did here is we created a Mutable Dictionary and setted Name and Mobile Number of particular Person by assiging them a specific key ("name") and "mobileNumber". And After assigning name and mobileNumber Key Values we added the dictionary in ContactsArray. if you don't know how to set an object to a dictionary and add Object to an Array you can Referit here

Now add Table View Delegates and dataSource Methods after viewDidLoad method.

#pragma mark UITableViewDataSource methodsb>

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
/* Returns the number of Sections you want in TableView. We will cover he details of Sections in Next Example */
    return 1;
}

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    /* returns the count of an array to determine how many rows should be available in one section */
    return [contactsArray count];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* Finds the object at index of an array and pass it to cell . I recommend to define Custom Cells like this . This is clean and clear. You may find various ways to design custom cells . But this is the best way.*/ 

    NSMutableDictionary  *contactsInfo = [contactsArray  objectAtIndex:indexPath.row];
    ContactsTableViewCell * cell = [ContactsTableViewCell cellForContact:contactsInfo inTableView:tableView];

    return cell;
}


- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
/* Returns the height of cell as defined in ContactsTableViewcell  remember to import this at top. */
    return [ContactsTableViewCell height];
}

  

You might see errors right now . Because we didn't created ContactsTableViewCell till Now. But just get the things clear here.

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    /* returns the count of an array to determine how many rows should be available in one section */
    return [contactsArray count];
}

numberOfRowsInSection method returns the number of Rows in particular section. Here we created an contactsArray which includes 5 entries like Jason,Steven , Jazzy,Lawren , Suzaen. So it returns count of 5.

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

cellForRowAtIndexPath is used to create a cell for a row at particular index. Index Starts with 0 always. You can create default cells using UITableViewCell as we created in Previous basic Example. But here we created a Custom cell named ContactsTableViewcCell and passed the dictionary which is accessed using contacts Array to Contacts cell. You understand this by practise.

.
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
heightForRowAtIndexpath method is used to give the heightto a particular row . if you write
if(indexPath.row == 2)
{
return 40;   /* Set Height = 40 for second row  */
}
else if (indexPath.row == 3)
{
  return 12;   /* set height= 12 for third row */
}

You can access any row of TableView using indexPath.row method

You can access any section using indexPath.section method .

We will cover the details of Sections in next Example

Time to create ContactsTableViewCell File where we add profleImage , Name and MobilenUmber of a Person

You will make it Fast with Practise. This is the first time that's why it's taking time for things to understand.

Goto File > New >File >Coca touch Class > Enter File Name (eg :- ContactsTableViewCell) > Next.


Now in ContactsTableViewcell.h file ,add this code and final ContactsTableViewcell.h file looks like this

//  ContactTableViewCell.h
//  Created by Manish Methani.
//  Copyright (c) 2017 Codzify. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "ContactsTableViewCell.h"

@interface ContactsTableViewCell : UITableViewCell
@property (nonatomic, strong) NSMutableDictionary *contactDictionary;

+ (ContactsTableViewCell *)cellForContact:(NSMutableDictionary *)contactDictionary inTableView:(UITableView *)tableView ;

+ (CGFloat) height;


@end

(ContactsTableViewCell *)cellForContact:(NSMutableDictionary *)contactDictionary inTableView:(UITableView *)tableView ;

We declared a custom Contacts cell here which accepts dictionary contactDictionary as a parameter.

Now in ContactsTableViewCell.m file , add this Code

//
//  ContactTableViewCell.m
//  Created by Manish Methani.
//  Copyright (c) 2017 Codzify. All rights reserved.
//

#import "ContactsTableViewCell.h"


static const CGFloat paddingX =12;

@interface ContactsTableViewCell ()
{
   UIImageView * contactImageView;
    UILabel *labelContactName;
    UILabel *labelDescription;
}


@end


@implementation ContactsTableViewCell

- (void)awakeFromNib {
    // Initialization code
    [super awakeFromNib];
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    
    // Configure the view for the selected state
}

+ (CGFloat) height
{
    return 72.0;
}


+ (ContactsTableViewCell *)cellForContact:(NSMutableDictionary *)contactDictionary inTableView:(UITableView *)tableView {
    ContactsTableViewCell *cell = (ContactsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:NSStringFromClass(self)];
    if (!cell) {
        cell = [[self alloc] initWithContactItem:contactDictionary];
    } else {
        cell.contactDictionary = contactDictionary;
      
    }
    return cell;
}


- (void)setContactDictionary:(NSMutableDictionary *)contactDictionary
{
    _contactDictionary = contactDictionary;
    
    labelContactName.text = [contactDictionary valueForKey:@"name"];
    labelDescription.text = [contactDictionary valueForKey:@"mobileNumber"];
}


- (id)initWithContactItem:(NSMutableDictionary *)contact
    {
    if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([self class])])
    {
        
        self.selectionStyle = UITableViewCellSelectionStyleNone;
        
        UIImageView *profileImage = [[UIImageView alloc]initWithFrame:CGRectMake(12, 4, 40, 40)];
        [self.contentView addSubview:profileImage];
        profileImage.image = [UIImage imageNamed:@"manish@2x.jpg"];
        profileImage.layer.masksToBounds = YES;
        profileImage.layer.cornerRadius = 40/2.0;
        
      
        labelContactName = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMaxX(profileImage.frame) +10, 4, 60, 24)];
        [self.contentView addSubview:labelContactName];
        labelContactName.textColor = [UIColor blackColor];
        labelContactName.font = [UIFont fontWithName:@"Helvetica-bold" size:16.0];
        
        
        labelDescription = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMaxX(profileImage.frame) + 8, CGRectGetMaxY(labelContactName.frame), 120, 24)];
        [self.contentView addSubview:labelDescription];
        labelDescription.textColor = [UIColor blackColor];
        labelDescription.font = [UIFont fontWithName:@"Helvetica" size:14.0];
        
        UIButton  *callButton = [[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width,self.frame.size.height - 32, 80, 32)];
        [self.contentView addSubview:callButton];
        [callButton setTitle:@"Call" forState:UIControlStateNormal];
        [callButton addTarget:self action:@selector(callBtnClicked) forControlEvents:UIControlEventTouchUpInside];
        [callButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        callButton.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:16.0];
        callButton.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:132.0/255.0 blue:180.0/255.0 alpha:1.0];
        callButton.layer.cornerRadius = 4.0;
        
        UIView *sepratorView = [[UIView alloc]initWithFrame:CGRectMake(6, CGRectGetMaxY(labelDescription.frame)+8, self.frame.size.width ,1 )];
        [self.contentView addSubview:sepratorView];
        sepratorView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];
        
        self.contactDictionary = contact;
    }
    
    return self;
}


#pragma mark - Actions
-(void)callBtnClicked
{
    NSLog(@"Call Button Clicked");
}


@end

Firstly this method is called

+ (ContactsTableViewCell *)cellForContact:(NSMutableDictionary *)contactDictionary inTableView:(UITableView *)tableView

Here we will create a reusable cell so that we can fix memory issues.Then we allocated initWithContactItem method here.

- (id)initWithContactItem:(NSMutableDictionary *)contact

This is the place where you can create Images, Labels , Buttons etc. All the Designing Stuff Goes here. At the end of this method you see self.contactDictionary = contact; This is used to set Dictionary of contact so that code knows what to set in Profile iMages , Name Labels , MobilenUmber Labels. You do not have to create Labels, Profile Images etc. every time . It's just one time process .DequecellWithReuseidentifier automatically takes care of it. Just pass a dictionary at the end.

- (void)setContactDictionary:(NSMutableDictionary *)contactDictionary
{
    _contactDictionary = contactDictionary;
    
    labelContactName.text = [contactDictionary valueForKey:@"name"];
    labelDescription.text = [contactDictionary valueForKey:@"mobileNumber"];
}

Then in this method we assign a name, rofile iMages and aMobile nUmber . You can customise as per your need . This is just the basic example how to create Custom Cells. Just get the concepts of how things are working . Which method is being called first and all.