In my code I'm using a tableview for which some of the methods are getting called, while others are not.
in initwithframe:
_table.delegate = self;
_table.dataSource = self;
this is called
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"TRAutocompleteCell";
id cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
cell = [_cellFactory createReusableCellWithIdentifier:identifier];
NSLog(#"got here 3");
NSAssert([cell isKindOfClass:[UITableViewCell class]], #"Cell must inherit from UITableViewCell");
NSAssert([cell conformsToProtocol:#protocol(TRAutocompletionCell)], #"Cell must conform TRAutocompletionCell");
UITableViewCell <TRAutocompletionCell> *completionCell = (UITableViewCell <TRAutocompletionCell> *) cell;
id suggestion = self.suggestions[(NSUInteger) indexPath.row];
NSAssert([suggestion conformsToProtocol:#protocol(TRSuggestionItem)], #"Suggestion item must conform TRSuggestionItem");
id <TRSuggestionItem> suggestionItem = (id <TRSuggestionItem>) suggestion;
[completionCell updateWith:suggestionItem];
return cell;
}
but this is not getting called in the same file.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"got here 5");
id suggestion = self.suggestions[(NSUInteger) indexPath.row];
NSLog(#"got here 4");
NSAssert([suggestion conformsToProtocol:#protocol(TRSuggestionItem)], #"Suggestion item must conform TRSuggestionItem");
self.selectedSuggestion = (id <TRSuggestionItem>) suggestion;
_queryTextField.text = self.selectedSuggestion.completionText;
[_queryTextField resignFirstResponder];
if (self.didAutocompleteWith)
self.didAutocompleteWith(self.selectedSuggestion);
}
The problem was not with the implementation, but my gesture recognition.
I have added a lister to my view to catch the tap event outside my text box, which was being called. I do not exactly know why these two are clashing, but when I removed it, it was working.
The tableview is for autocompletion in my textbox.
Did You do:
yourTableView.delegate = self;
yourTableView.dataSource = self;
While creating the tableView.
If it is .xib file , did you connect the delegate and dataSource?
Related
I'm using two different custom table cells in my table view. That said, I only want didSelectRowAtIndexPath to fire when, for example, Custom Cell Identifier 2 is tapped (but not when Custom Cell Identifer 1 is tapped). How might I go about this? See code below. Right now, didSelectRowAtIndexPath fires when either cell is tapped...
- (void)viewDidLoad {
[super viewDidLoad];
static NSString *ChatTableIdentifier = #"ChatTableViewCell";
static NSString *ChatTableIdentifier2 = #"SwapDetailTableViewCell";
UINib *nib = [UINib nibWithNibName: ChatTableIdentifier bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier: ChatTableIdentifier];
UINib *nib2 = [UINib nibWithNibName: ChatTableIdentifier2 bundle:nil];
[self.tableView registerNib:nib2 forCellReuseIdentifier: ChatTableIdentifier2];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = self.messages[indexPath.row];
id swaptime = data[#"swaptime"];
if ([swaptime isKindOfClass:[NSString class]]) {
static NSString *ChatTableIdentifier2 = #"SwapDetailTableViewCell";
SwapDetailTableViewCell *cell = (SwapDetailTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier2 forIndexPath:indexPath];
NSString *time = data[#"swaptime"];
cell.startTime.text = time;
NSString *timeEnd = data[#"endswaptime"];
cell.endTime.text = timeEnd;
return cell;
} else {
static NSString *ChatTableIdentifier = #"ChatTableViewCell";
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
NSString *userName = data[#"name"];
cell.sendingUser.text = userName;
NSString *messageBody = data[#"body"];
cell.messageDisplayed.text = messageBody;
NSString *timeReceived = data[#"published at"];
cell.timeStamp.text = timeReceived;
return cell;
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SwapDetailsViewController *detailViewController = [[SwapDetailsViewController alloc]
initWithNibName:#"SwapDetailsViewController" bundle:nil];
detailViewController.swapDetails = [self.messages objectAtIndex:indexPath.row];
[self presentViewController:detailViewController animated:YES completion:nil];
}
You can put an if condition inside didSelectRowAtIndexPath as the one you put in cellForRowAtIndexPath
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *data = self.messages[indexPath.row];
id swaptime = data[#"swaptime"];
//perform action based on this, and don't do anything in second case
if ([swaptime isKindOfClass:[NSString class]])
//this indexPath will always contain cell of one kind
}else{
// this indexPath will contain second cell kind
}
}
Or alternatively you can also do-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// this will give you cell at the selected indexPath
id cell = [tableView cellForRowAtIndexPath:indexPath]
//perform action based on this, and don't do anything in second case
if ([cell isKindOfClass:[SwapDetailTableViewCell class]])
}else{
}
}
If you don't want a particular row to be selected, the proper solution is to implement the tableView:willSelectRowAtIndexPath: delegate method and return nil for rows you don't want selected.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (some condition) {
return nil;
} else {
return indexPath;
}
}
So now you need to determine the proper condition. I can think of three ways in your case:
Use the same if condition you have in cellForRowAtIndexPath:.
Get the row's cell and look at the cell's class.
Get the row's cell and look at the cell's reuse identifier.
Option 1 isn't ideal because if you change the logic in cellForRowAtIndexPath you have to remember to change it here too. Options 2 and 3 are essentially the same in this case. I'd go with option 3.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell.reuseIdentifier isEqualToString: ChatTableIdentifier) {
return nil;
} else {
return indexPath;
}
}
Since you are now using ChatTableIdentifier in three places, I would move the lines:
static NSString *ChatTableIdentifier = #"ChatTableViewCell";
static NSString *ChatTableIdentifier2 = #"SwapDetailTableViewCell";
to before the #implementation line and remove them from everywhere else in the file. No sense in recreating the same variables over and over.
In didSelectRowAtIndexPath you can call method
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
for the indexPath of a current row and get the cell you are currently selecting. Every cell has a reuse identifier, so you can check the identifier of the cell you are selecting. if it equals "SwapDetailTableViewCell", then you can perform everything you need(for example move to another viewController)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell.reuseIdentifier isEqual: #"SwapDetailTableViewCell"]) {
SwapDetailsViewController *detailViewController = [[SwapDetailsViewController alloc]
initWithNibName:#"SwapDetailsViewController" bundle:nil];
detailViewController.swapDetails = [self.messages objectAtIndex:indexPath.row];
[self presentViewController:detailViewController animated:YES completion:nil];
}
I have two different tableViews in one view controller and I get an error message. The data source and delegate are set to the view controller. Am I doing something wrong in the tableview methods. I haven't dealt with more than one tableView in the same view before. Thanks
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
if (tableView == self.postsTableView) {
return 1;
}
else if (tableView == self.eventsTableView){
return 1;
}
return 1;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (tableView == self.postsTableView) {
return 1;
}
else if (tableView == self.eventsTableView){
return 1;
}
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if ([tableView isEqual: self.postsTableView]) {
profilePagePostCell *cellOne = (profilePagePostCell *) [tableView dequeueReusableCellWithIdentifier:#"profilePostCell"];
cellOne.postLabel.text = [NSString stringWithFormat:#"Hi"];
return cellOne;
}
if ([tableView isEqual: self.eventsTableView]) {
profileEventCell *cellTwo = (profileEventCell *) [tableView dequeueReusableCellWithIdentifier:#"profileEventCell"];
cellTwo.eventLabel.text = [NSString stringWithFormat:#"The big One"];
return cellTwo;
}
profileEventCell *cell = (profileEventCell *) [tableView dequeueReusableCellWithIdentifier:#"profileEventCell"];
return cell;
}
Unless you have already called registerClass:forCellReuseIdentifier: or defined a prototype class in your nib or storyboard on your UITableView, you're going to get nil cells back from that dequeReusableCellWithIdentifier: call. You can register one if you like, or you can create a local instance when you have received nil, making sure to call UITableViewCell initWithStyle:reuseIdentifier: as your initialization method.
I'm not seeing any immediate problems in your code. I assume those cells are registered properly in either a prototype cell or by registering them in code. If not then that is probably the issue. I would put a breakpoint in your cellForRowAtIndexPath: and step through the process to make sure its even being called for one, and make sure that it is in fact returning a cell. Make sure that the cell is not nil in all of your cases.
Not enough information but two among several reasons:
(1) You've not registered the two custom cells before trying to deque them.
If this is the case, register them while overridng viewDidLoad like follows.
[self.postsTableView registerClass:[profilePagePostCell class] forCellReuseIdentifier:#"profilePostCell"];
or
[self.postsTableView registerNib:#"YOUR_NIB_NAME" forCellReuseIdentifier:#"profilePostCell"]
(2) The identifier names you're using in cellForRowAtIndexPath method do not match what you registered in viewDidLoad method.
Double check the names and I strongly recommend you to use defined constant name in order to get support from Xcode.
You don't alloc memory for cells. Try this code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath(NSIndexPath *)indexPath{
static NSString *cellIdentifierPosts = #"cellIdentifierPosts"
static NSString *cellIdentifierEvents = #"cellIdentifierEvents"
if ([tableView isEqual: self.postsTableView]) {
profilePagePostCell *cellOne = (profilePagePostCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifierPosts];
if (!cellOne)
cellOne = [[profilePagePostCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: cellIdentifierPosts];
cellOne.postLabel.text = [NSString stringWithFormat:#"Hi"];
return cellOne;
}
else if ([tableView isEqual: self.eventsTableView]) {
profileEventCell *cellTwo = (profileEventCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifierEvents];
if (!cellTwo)
cellTwo = [[profileEventCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: cellIdentifierEvents];
cellTwo.eventLabel.text = [NSString stringWithFormat:#"The big One"];
return cellTwo;
}
else{
profileEventCell *cell = (profileEventCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifierEvents];
if (!cell)
cell = [[profileEventCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: cellIdentifierEvents];
cell.eventLabel.text = [NSString stringWithFormat:#"The default One"];
return cell;
}
}
I'm seeking how create several cells to go to different ViewControllers.
For my TableView, I'm using a subclass of UITableViewController.
And when I choose 2 in the following method, I just see 2 identical cells which are doing exactly the same thing. I'm not interested by this. I don't even know their IndexPath in order to change their title.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 2;
}
And When I try to put another UITableViewCell in my TableView, it doesn't appear on iOS simulator, even with the same option (same subclass) than my first UITableViewCell which I can see.
Thanks for your help.
Edit : Here is my new code to create 2 cells but doesn't work :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell2";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] init];
}
static NSString *CellIdentifier1 = #"Cell1";
UITableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if (cell1 == nil) {
cell1 = [[customCell alloc] init];
}
// Configure the cell...
return cell;
}
You define your cells in tableView:cellForRowAtIndexPath:, so you should provide an implementation for that method.
tableView:numberOfRowsInSection: only returns the number of cells in the table.
If you need more help, please provide your implementation for tableView:cellForRowAtIndexPath:. This is how a typical implementation looks like:
- (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] autorelease];
}
... customize your cell ...
}
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell2";
static NSString *CellIdentifier1 = #"Cell1";
if(indexPath.row == 0 ) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] init];
}
} else {
UITableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if (cell1 == nil) {
cell1 = [[customCell alloc] init];
}
}
return cell;
}
This method gets called when a cell has been selected. You can decide what you wanna do according to the selected row
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0)
[self goToFirstViewController];
else
if(indexPath.row == 1)
[self goToSecondViewController];
}
Use the following:
- (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section
This delegate method returns the number of rows you want in that particular section. So if you want more than 2 rows, or you want the number of rows to be dynamic, you can create a NSArray in the AppDelegate or in the init method of the viewController class, and return the number in the numberOfRowsInSection method like
return [delegate numberOfNames];
In my example above, I created an array in my AppDelegate and also a method to return the number of objects I have in that array so that I can create the number of rows for my table.
- (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) indexPath
This delegate method will show what you want to display in each cell. Therefore, following on from my array created in my AppDelegate, I first create the cell, then I will set the text I want to display on the cell with a method I created in my AppDelegate that will return a NSString while taking in a NSInteger so that I can loop through my array and display the text accordingly.
static NSString* MyIdentifier = #"Default";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if( cell == nil )
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
cell.textLabel.text = [delegate nameAtIndex:indexPath.row];
}
nameAtIndex is the name of the method I created in my AppDelegate that will return the NSString object at the specific index (ie. the row number) from the NSArray I created to store all the items of my table.
When the user clicks on any of the rows in the table created, this delegate method will be called
- (void) tableView: (UITableView*) tableView didSelectRowAtIndexPath: (NSIndexPath*) indexPath
And in here, I will check if the text displayed matches any of the items in my array from the AppDelegate that stores the items in the table, and create the view that is necessary.
UIViewController* viewController = nil ;
NSString* nameInArray = [delegate nameAtIndex:indexPath.row] ;
if( [nameInArray isEqualToString:#"firstName"] )
{
viewController = [[FirstViewController alloc] init];
}
else if( [nameInArray isEqualToString:#"secondName"] )
{
viewController = [[SecondViewController alloc] init];
}
else if( [nameInArray isEqualToString:#"thirdName"] )
{
viewController = [[ThirdViewController alloc] init];
}
So with these 3 delegate methods, you will be able to create the table using a NSArray created, and be able to redirect the user to a viewController according to which option in the table he chooses. You will not have to keep editing the delegate methods if you choose to add more rows to the table as well since you are returning the count of the array when setting up the table.
The array and methods to get the data of the array can be created in the viewController as well, not necessarily in the AppDelegate, in case you were wondering.
The methods are as follows:
-(NSInteger) numberOfNames
{
return [myArray count];
}
-(NSString*) nameAtIndex: (NSInteger) index
{
return [myArray objectAtIndex:index] ;
}
Hope this helps! :)
I am having issues with my tableView not firing the didSelectRowAtIndexPath method. I have implemented the delegates as such:
#interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate,UIScrollViewDelegate>
And in my storyboard the tableView's data source and delegate are both pointed at the base View Controller. I have User Interactions enabled as well as Selection set to Single Selection, and it is not the TapGesture problem since my tap gestures are not bound to the view and I have checked and they do not fire.
This is the code for setting up the table:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return menuArray.count;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"menuCell"];
NSDictionary *menuItem = [menuArray objectAtIndex:indexPath.row];
cell.textLabel.text = menuItem[#"Title"];
cell.detailTextLabel.text = menuItem[#"Subtitle"];
return cell;
}
-(void)showMenu{
[UIView animateWithDuration:.25 animations:^{
[content setFrame:CGRectMake(menuTable.frame.size.width, content.frame.origin.y, content.frame.size.width, content.frame.size.height)];
}];
}
-(void)hideMenu{
[UIView animateWithDuration:.25 animations:^{
[content setFrame:CGRectMake(0, content.frame.origin.y, content.frame.size.width, content.frame.size.height)];
}];
}
-(IBAction)showMenuDown:(id)sender {
if(content.frame.origin.x == 0)
[self showMenu];
else
[self hideMenu];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//whatever
}
The table is initially out of view on the storyboard (origin.x is set to -150), then when the user clicks on a button in the navigationBar, the view slides over to reveal it, which is what might be causing the problem I think.
Is there anything wrong with my code or implementation that would be causing this to not work?
If you already see your table populated with values from your dictionary then you can rule out data source and delegate as being the problem. i.e. your storyboard connections are working.
Your code looks fine to me. the only difference I see is I usually define my table like this. Try this and see if it helps.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(#"Inside cellForRowAtIndexPath");
static NSString *CellIdentifier = #"Cell";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil)
{
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Your code here
// ....
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"menuCell"];
This will return nil in case there was never a cell created.
so checking if cell is nil is mandatory and if so, you need to create a cell.
static NSString *CellIdentifier = #"menuCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
as you are using storyboard you can alternatively use
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
for prototype cells. Make sure you use the same identifier in the storyboard and that you registered your the cell's class
- (void) viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"menuCell"];
}
It appears my tableview delegate won't show my image at all. The image shows fine on my UIImageView that I added to my view controller. I am using the table view from interface builder if this bit of information helps.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView setDelegate:self];
[tableView setDataSource:self];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.accessoryView = [[UIImageView alloc] initWithImage:myimage];
return cell;
}
Try setting the delegate and data source via interface builder, Or in the viewDidLoad method.
-(void)viewDidLoad{
self.tableView.delegate = self;
self.tableView.dataSource = self;
//...
}
Also, make sure that the myImage object is not nil
If i can remember correctly, default style has an imageView property. Try:
cell.imageView.image = [UIImage imageNamed:#"image.png"];
You need to set the delegate and datasource in your viewDidLoad method and within your .h file in stead of the delegate method of the tableview itself.
EDIT:
Also make sure that all your delegates methods are added
Those delegates are:
// Return the number of sections
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Return the number of rows
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
//filling of tableview
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
}
//pressing of a row
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}