I am having a serious issue with UITableView reloadData method. I have a UIViewController class, WiGiMainViewController that has a UITableView and NSMuttableArray in it. I am currently issuing network calls in the AppDelegate, and posting notifications to the WiGiMainViewController once the data has been downloaded. In my selector method for the notification, reloadWigiList, I am passing an NSArray containing the the recently downloaded items. I then initialize the WiGiMainViewController's NSMuttableArray with the passed in array and proceed to call reloadData() on my UITableView object. I can see from nslog statements that the numberOfRowsInSection is fired on reload but not the cellForRowAtIndexPath, therefore causing the UI NOT to reload the UITableView with the newly downloaded items. I have verified that the reloadData method is being called on the main thread and that the datasource delegate are set in IB and programatically in the viewDidLoad method of WiGiMainViewController. Any ideas why my UITableView, wigiLists isn't reloading the data, in particular, not calling the cellForRowAtIndexPath method?
#interface WiGiMainViewController : UIViewController<FBRequestDelegate,UITableViewDelegate, UITableViewDataSource> {
//setup UI
UITableView *wigiLists;
WiGiAppDelegate *myAppDelegate;
NSMutableArray *itemsList;
}
#property (nonatomic, retain) WiGiAppDelegate *myAppDelegate;
#property (nonatomic, retain) IBOutlet UITableView *wigiLists;
#property (nonatomic, retain) NSMutableArray *itemsList;
-(void) reloadWigiList: (NSNotification*) notification;
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView;
-(NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection:(NSInteger) section;
-(UITableViewCell *) tableView: (UITableView*)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath;
#end
#implementation WiGiMainViewController
#synthesize headerLabel = _headerLabel, userNameLabel = _userNameLabel, facebookPicture = _facebookPicture,
myAppDelegate, wigiLists, itemsList;
- (void)viewDidLoad {
NSLog(#"In viewDidLoad");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadWigiList:) name:#"wigiItemUpdate" object:nil];
// get appdelicate
self.myAppDelegate = (WiGiAppDelegate*) [[UIApplication sharedApplication] delegate];
self.itemsList = [[NSMutableArray alloc] init];
//setup tableview
self.wigiLists = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self.wigiLists.delegate = self;
self.wigiLists.dataSource = self;
//set up view
[self.headerLabel setText:self.myAppDelegate.HEADER_TEXT];
//check if user is logged in
if (self.myAppDelegate.isLoggedIn) {
//user is logged in
NSLog(#"HERE");
//get facebook information to populate view
[self retrieveFacebookInfoForUser];
//[self.myAppDelegate retrieveWigiItems];
}else {
//user is not logged in
NSLog(#"user not logged in");
//show login modal
}
//[self.wigiLists reloadData];
[super viewDidLoad];
}
-(void) reloadWigiList: (NSNotification *) notification {
if ([NSThread isMainThread]) {
NSLog(#"main thread");
}else{
NSLog(#"METHOD NOT CALLED ON MAIN THREAD!");
}
NSLog(#"notification recieved:%#", notification.userInfo);
NSLog(#"in reloadwigilists:%#", wigiLists );
NSLog(#"list size:%#", self.itemsList);
NSLog(#"delegate:%#",self.wigiLists.delegate);
NSLog(#"datasource:%#",self.wigiLists.dataSource);
//populate previously empty itemsList
[self.itemsList setArray:notification.userInfo];
NSLog(#"list size:%#", self.itemsList);
[self.wigiLists reloadData];
}
/////////////////////////////////////
// UITableViewDelegate protocols
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
//NSLog(#"numberofsections: %#", [self.itemsList count]);
return 1;
}
-(NSInteger) tableView: (UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
NSLog(#"7t87giuiu%#",self.itemsList);
NSLog(#"numberofrows: %i", [self.itemsList count]);
return [self.itemsList count];
//return 6;
}
first thing i would do is set a breakpoint on [self.wigiMainViewController.wigiLists reloadData]
if the wigiLists tableView shows a null in the debugger then it is probably an outlet that hasn't been set. also, make sure that both the delegate and datasource have been set.
Wow! after 3 days of banging my head against this problem, it was something ridiculously simple. In my ViewDidLoad method of WiGiMainViewController, i was initializing my tableview:
self.wigiLists = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
Because I had linked the tableView I created in IB to my instance wigiLists, alloc'ing and initializing in the ViewDidLoad overwrote my link to the tableView created in IB and currently being displayed. Getting rid of this line fixed everything.
Related
I wanna reload some data in my FAQViewcontroller. The tableview is placed in a nib file (view) which is loaded with the Viewcontroller, called this way:
FAQViewController *vc = [[FAQViewController alloc] initWithNibName:#"FAQViewController" bundle:nil];
[self presentViewController:vc animated:YES completion:nil];
In viewDidLoad of FAQViewcontroller, the tableview is displaying the right way (I see the separator lines and when I'm changing the backgroundColor, it is working well). But there's no information to display yet, so the cells are empty (as expected).
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableView registerNib:[UINib nibWithNibName:#"FAQCell" bundle:nil] forCellReuseIdentifier:#"FAQCell"];
if (self.faqData == nil || [_faqData count] == 0) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[[DataModel sharedInstance] getFrequentlyAskedQuestions:^(NSError *error) {
}];
});
}
}
When the network call is done, this function in the FAQViewcontroller is called:
- (void)setFaqData:(NSArray *)faqData {
_faqData = faqData;
NSLog(#"%#", _faqData);
dispatch_async(dispatch_get_main_queue(), ^() {
[self.tableView reloadData];
});
}
But reloadData is doing nothing. The numberofrowsinsection isn't called.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _faqData.count;
}
I don't know what to do about it. Never had this before (normally I code in Swift, maybe I forget something in Objective-C)?
Does anyone know what I'm doing wrong?
Thank you very much!
Could you try making tableView as a strong reference?
something like:
#property (nonatomic, strong) IBOutlet UITableView *tableView
1) I started a new project in xcode using the single view application.
2) I deleted the default view controller and added a new UITableViewController
3) In storyboard, I dragged out a UITableViewController and set it to the one I just created
4) Set the reuse identifier
In my code I tried to override the init method to do some setup. Why is my custom init method not being called? When you are using storyboard, and you drag out a UITableViewController and set it to a custom class, can you not override the initWithStyle: method? When I put the setup in viewDidLoad then it worked.
Here is the code for the view controller:
#import "ItemsViewController.h"
#import "BNRItem.h"
#import "BNRItemStore.h"
#implementation ItemsViewController
- (id)init
{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
for (int i = 0; i < 10; i++) {
[[BNRItemStore defaultStore] createItem];
NSLog(#"Test init");
}
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
NSLog(#"test init style");
return [self init];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
NSLog(#"test tableview rowsinsection");
return [[[BNRItemStore defaultStore] allItems] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"test tableview cellforrow");
// Create an instance of UITableViewCell, with default appearance
// Check for a reusable cell first, use that if it exists
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"itemsCell"];
// If there is no reusable cell of this type, create a new one
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"itemsCell"];
}
// Set the text on the cell with the description of the item
// that is at the nth index of items, where n = row this cell
// will appear in on the tableview
[[cell textLabel] setText:#"Hello"];
return cell;
}
#end
init is only called when you using [[class alloc] init]
you can override
- (void)awakeFromNib
awakeFromNib
Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.
- (void)awakeFromNib
Discussion
An awakeFromNib message is sent to each object loaded from the archive, but only if it can respond to the message, and only after all the objects in the archive have been loaded and initialized. When an object receives an awakeFromNib message, it is guaranteed to have all its outlet instance variables set.
Im writing an app where someone adds a contact to the app, giving their name, number and photo. Then this information is displayed in a table, with each individual contact on a different cell and when the user presses on the cell it will call the number that was typed in for the contact. I have put in a large button on each of the cells for the user to press. This is the code
PictureListMainTable.m
#import "PictureListMainTable.h"
#import "PictureListDetail.h"
#import "CoreDataHelper.h"
#import "Pictures.h"
#implementation PictureListMainTable
#synthesize managedObjectContext, pictureListData, callButton;
// When the view reappears, read new data for table
- (void)viewWillAppear:(BOOL)animated
{
// Repopulate the array with new table data
[self readDataForTable];
}
// Grab data for table - this will be used whenever the list appears or reappears after an add/edit
- (void)readDataForTable
{
// Grab the data
pictureListData = [CoreDataHelper getObjectsForEntity:#"Pictures" withSortKey:#"title" andSortAscending:YES andContext:managedObjectContext];
// Force table refresh
[self.tableView reloadData];
}
#pragma mark - Actions
// Button to log out of app (dismiss the modal view!)
- (IBAction)logoutButtonPressed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
#pragma mark - Segue methods
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get a reference to our detail view
PictureListDetail *pld = (PictureListDetail *)[segue destinationViewController];
// Pass the managed object context to the destination view controller
pld.managedObjectContext = managedObjectContext;
// If we are editing a picture we need to pass some stuff, so check the segue title first
if ([[segue identifier] isEqualToString:#"EditPicture"])
{
// Get the row we selected to view
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
// Pass the picture object from the table that we want to view
pld.currentPicture = [pictureListData objectAtIndex:selectedIndex];
}
}
#pragma mark - Table view data source
// Return the number of sections in the table
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Return the number of rows in the section (the amount of items in our array)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [pictureListData count];
}
// Create / reuse a table cell and configure it for display
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Get the core data object we need to use to populate this table cell
Pictures *currentCell = [pictureListData objectAtIndex:indexPath.row];
// Fill in the cell contents
cell.textLabel.text = [currentCell title];
cell.detailTextLabel.text = [currentCell desc];
int number;
number = [currentCell desc];
-(IBAction)MakePhoneCall:(id)sender {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"tel:",number]];
}
// If a picture exists then use it
if ([currentCell smallPicture])
{
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
cell.imageView.image = [UIImage imageWithData:[currentCell smallPicture]];
}
else{
}
return cell;
}
// Swipe to delete has been used. Remove the table item
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Get a reference to the table item in our data array
Pictures *itemToDelete = [self.pictureListData objectAtIndex:indexPath.row];
// Delete the item in Core Data
[self.managedObjectContext deleteObject:itemToDelete];
// Remove the item from our array
[pictureListData removeObjectAtIndex:indexPath.row];
// Commit the deletion in core data
NSError *error;
if (![self.managedObjectContext save:&error])
NSLog(#"Failed to delete picture item with error: %#", [error domain]);
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
-(IBAction)MakePhoneCall:(id)sender {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"tel:",number]];
}
#end
PictureListMainTable.h
#import <UIKit/UIKit.h>
#interface PictureListMainTable : UITableViewController
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) NSMutableArray *pictureListData;
#property (nonatomic, retain) IBOutlet UIButton *callButton;
-(IBAction)MakePhoneCall:(id)sender;
- (void)readDataForTable;
#end
Where should I place the IBaction and why isint it working at the moment where it is and how can I make it work?
There are a couple of approaches you could take to achieve this. But firstly, I don't understand what you are doing at the bottom of -tableview:cellForRowAtIndexPath:. It's as if you are trying to define your IBAction method inside this method. You also have it defined at the bottom of the implementation, but in that method the number variable is not in scope.
Anyway, you should subclass the UITableViewCell. In the implementation for the subclass, you should define the IBAction method and hook it up in interface builder, or otherwise.
When the button is tapped, you should hand the number for the selected cell back to the PictureListMainTable view controller, in order for that view controller to process it (i.e. call the number). You can do this in two ways:
1) the delegate method
Create a protocol, defined in the header file for your subclass of UITableViewCell. And make the main view controller conform to this protocol. Set the cell's delegate to the main view controller. In the implementation of the cell subclass, call this delegate method. For example:
the header file for the UITableViewCell subclass "PictureListMainTableCell.h"
#protocol PictureListMainTableCellDelegate;
#interface PictureListMainTableCell : UITableViewCell
#property (nonatomic, copy) NSString *telephoneNumber;
#property (nonatomic, weak) id<PictureListMainTableCellDelegate> delegate;
#end
#protocol PictureListMainTableCellDelegate
-(void)pictureListMainTableCell:(PictureListMainTableCell *)cell wantsToCallNumber:(NSString *)number;
#end
the implementation file "PictureListMainTableCell.m"
#import "PictureListMainTableCell.h"
#implementation PictureListMainTableCell
-(IBAction)MakePhoneCall:(id)sender
{
//send the delegate the number to call.
[self.delegate pictureListMainTableCell:self wantsToCallNumber:self.telephoneNumber];
}
#end
Above, in the MakePhoneCall method, we call -pictureListMainTableCell:wantsToCallNumber: on the delegate. In this case, the delegate is your main view controller. We will set this below.
Setting the cell's delegate: In your main view controller file (PictureListMainTable.m), in the -tableView:cellForRowAtIndexPath: method, set the delegate on the cell to self. e.g.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// get the cell...
PictureListMainTableCell *cell = // dequeue the cell
// do some other setting up...
// set the delegate on the cell
cell.delegate = self;
// set the telephoneNumber variable on the cell, for example...
cell.telephoneNumber = [currentCell desc];
return cell;
}
Now you need to make sure self implements the delegate method. So still in PictureListMainTable.m, you need to define the method as follows:
#pragma mark - PictureListMainTableCellDelegate methods
-(void)pictureListMainTableCell:(PictureListMainTableCell *)cell wantsToCallNumber:(NSString *)number
{
NSString *urlString = [NSString stringWithFormat:#"tel://%#", number];
NSLog(#"calling telephone number [%#]", number);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
}
You should also specify that the PictureListMainTable class conforms to your new protocol, as well as the UITableViewDataSource protocol. Add a private category on PictureListMainTable as follows (at the top of the implementation file, after the imports, before #implementation):
#interface PictureListMainTable () <UITableViewDataSource, PictureListMainTableCellDelegate>
#end
(this extends the PictureListMainTable interface. It only extends it to specify privately that it conforms to these protocols.)
2) the NSNotification method
While I was typing out the above explanation, I decided it's my preferred way of doing things, so I would recommend doing it like that. There is the option of posting an NSNotification form your cell subclass, and observing for this notification from your main view controller. Just look into NSNotificationCenter, the following methods:
–postNotificationName:object:userInfo: (send the number in userInfo dictionary). Listen for it using –addObserver:selector:name:object:.
But like I said, option 1 is better, in my opinion.
Let me know if anything is unclear, good luck :)
EDIT: I really recommend reading this blog post to understand delegation: http://alexefish.com/post/15966868557/understanding-and-creating-delegates-in-objective-c
I am calling a method in my TableViewController class from another class.
To call the method of displaying the tableview, I do this:
TableViewController *tableVC = [[TableViewController alloc]init];
[tableVC setTableViewContent];
then in TableViewController.h
#interface TableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
{
NSMutableArray *nameArray;
}
-(void)setTableViewContent;
#property (nonatomic, strong) IBOutlet UITableView *tableView;
#end
TableViewController.m
#implementation TableViewController
#synthesize tableView;
- (void)viewDidLoad
{
nameArray = [[NSMutableArray alloc]init];
[super viewDidLoad];
}
-(void)setTableViewContent{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
for(int i=0;i< [appDelegate.businessArray count];i++)
{
NSDictionary *businessDict = [[appDelegate.businessArray objectAtIndex:i] valueForKey:#"location"];
nameArray = [appDelegate.businessArray valueForKey:#"name"];
}
NSLog(#"%#", nameArray);
NSLog(#"tableview: %#", tableView);
// here tableview returns null
[tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [nameArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"updating tableview...");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = [nameArray objectAtIndex:indexPath.row];
return cell;
}
For some reason when I try to log the tableview, it returns null, so the ReloadData doesn't work. The delegate and datasource is connected properly in IB, and there is a referencing outlet for tableView.
Any idea what is going on here? Thanks in advance
If you added the table view controller to a container view, then you can get a reference to that controller in prepareForSegue. For a controller in a container view, prepareForSegue will be called right before the parent controller's viewDidLoad, so you don't need to do anything to invoke it. In my example below, I've called the segue "TableEmbed" -- you need to give the segue that identifier in IB.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"TableEmbed"]) {
TableViewController *tableVC = (TableViewController *)segue.destinationViewController;
[tableVC setTableViewContent];
}
}
Be aware that prepareForSegue:sender: is called before either controller's viewDidLoad is called, so you should move the initialization of your array to setTableViewContent, and your reloadTable should go into viewDidLoad.
BTW, it's not clear to me why you want to call setTableContent from your other class anyway. Why not move all the code in that method to the viewDidLoad method of the table view controller?
This is happening because you are calling a method on tableView before it actually exists. Simply initializing that class doesn't draw the table itself, so using reloadData before the table has actually been created doesn't really make any sense.
What you want to do in this situation is create your nameArray in whatever class is calling setTableViewContent, and then pass it in either via a custom init method, or by setting tableVC.nameArray before loading that table view controller.
What I would do is make custom init method like - (id)initWithArray:(NSMutableArray *)nameArr
Which should look something like this:
if (self = [super init]) {
nameArray = [nameArr copy];
}
return self;
Then where you have TableViewController *tableVC = [[TableViewController alloc]init]; put TableViewController *tableVC = [[TableViewController alloc]initWithArray:theNameArray]; where theNameArray is the content in setTableViewContent (which you are now generating in the same class that calls the table view instead of in the table view itself).
Make sense?
I solved a similar situation by creating a "safe" reload method on the UITableViewController:
- (void)reloadTableViewData
{
if ([self isViewLoaded])
[self.tableView reloadData];
}
According to the docs for isViewLoaded:
Calling this method reports whether the view is loaded. Unlike the view property, it does not attempt to load the view if it is not already in memory.
Therefore it is safe to call reloadTableViewData on the table view controller at any time.
I already searched Google the entire day but I just can't figure out a solution for this one.
I am trying to implement a table view with custom cells in my iOS App. I'm using custom cells that display different images and Labels. Everything is working fine except that the Cells are too large for the table view. I know that I need to implement the heightForRowAtIndexPath method but it is never called.
I've tried setting the delegate for the TableView in the nib file and in the ShowPostsViewController in the code but nothing helps. I expect that the problem probably is that the dataSource is set but not the delegate. I can't understand why though.
Every solution I found until now says that the delegate is not set correctly. However, I'm pretty sure it is in my case?!
I'm grateful for any help. Here's my code:
ShowPostsViewController.h
#interface ShowPostsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) IBOutlet UITableView *postsTableView;
#end
And ShowPostsViewController.m
#implementation ShowPostsViewController
#synthesize postsTableView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.postsTableView.delegate = self;
self.postsTableView.dataSource = self;
NSLog(#"Delegate set");
[postsTableView beginUpdates];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for(int i=0; i<8; i++){
[tempArray addObject: [NSIndexPath indexPathForRow:i inSection:0]];
}
[postsTableView insertRowsAtIndexPaths:tempArray withRowAnimation:UITableViewRowAnimationAutomatic];
NSLog(#"Updates Called");
[postsTableView endUpdates];
[postsTableView reloadData];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 8;
}
//PostTableViewCell is my custom Cell that I want to display
-(PostTableViewCell*)tableView: (UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
PostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(!cell){
cell = [[PostTableViewCell alloc]initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:#"cell"];
}
return cell;
}
//This method is not called for some reason
-(CGFloat)tableview: (UITableView*)tableView heightForRowAtIndexPath: (NSIndexPath*) indexPath{
NSLog(#"Height Method called");
CGFloat returnValue = 1000;
return returnValue;
}
//This method is called
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView{
NSLog(#"Section Number called");
return 1;
}
#end
I've also got the tableView linked to the ShowPostsViewController in the Interface Builder.
Thank you all for your great support.
You are going to kick yourself for this mistake. You have implemented the method:
-(CGFloat)tableview: (UITableView*)tableView heightForRowAtIndexPath: (NSIndexPath*) indexPath
But the actual delegate method should be:
-(CGFloat)tableView: (UITableView*)tableView heightForRowAtIndexPath: (NSIndexPath*) indexPath
The only difference is the capital V in tableView. You have a lowercase v by mistake.
Try to make use of as much code completion in Xcode as you can to help avoid these types of mistakes.