I am creating my own custom UITableViewCell using interface builder. I am supporting iOS 5 & iOS 6 but I do not want to use Storyboard. Please do not suggest storyboard. I'm sticking to Interface Builder and writing programatically.
I created a class that subclasses UITableViewCell. Here's the .h file:
#interface CategoryCell : UITableViewCell
{
__weak IBOutlet UIImageView *image;
__weak IBOutlet UILabel *name;
__weak IBOutlet UILabel *distance;
__weak IBOutlet UILabel *number;
__weak IBOutlet UIImageView *rating;
}
#property (nonatomic, weak) IBOutlet UIImageView *image;
#property (nonatomic, weak) IBOutlet UILabel *name;
#property (nonatomic, weak) IBOutlet UILabel *distance;
#property (nonatomic, weak) IBOutlet UILabel *number;
#property (nonatomic, weak) IBOutlet UIImageView *rating;
#end
The XIB file is of type UIViewController and has a View of type CategoryCell. I connected the outlets as I have to.
The Problem
dequeueResuableCellWithIdentifier is not calling the custom cell. Here's what I have:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CategoryCell";
CategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CategoryCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
.....
}
return cell
}
When I substitute the loadBundle line with: cell = [[CategoryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];, it does work. But the nib does not load. So a cell loads, but not my own cell so I cannot set the labels and images that I want. When adding the regular load bundle line (as the sample above shows) and breakpoint the init method of the custom cell, it does not get called. Also, what I get is a full white screen that overrides the whole iPhone screen in the simulator.
Why's that happening? What am I doing wrong here? When I tried setting the outlets to strong (which I know I'm not supposed to), it does not work either.
EDIT:
I fixed it by replacing the NSBundle line with:
UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:#"CategoryCell" bundle:nil];
cell = (CategoryCell *)temporaryController.view;
What is it that I've done wrong with the NSBundle method? Supposedly that's supposed to be the "easier" way to do it.
Have you ever try the registerNib method in tableview? It's very convenient for load nib start from iOS 5.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:#"CategoryCell" bundle:nil]
forCellReuseIdentifier:#"CategoryCell"];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CategoryCell";
CategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
return cell
}
make sure you have define identifier in the CategoryCell.nib , it's under Attribute inspector! Hope this work.
The XIB file is of type UIViewController and has a View of type CategoryCell. I connected the outlets as I have to.
The problem in this is that the root object of your XIB need to be a UITableViewCell or a subclass of it.
And be sure to have your reuse identifier set in your XIB.
Related
I keep running into a problem when I try to write to a label in a custom cell. So right now, I have a class with a table view called "AccountViewController" and I have a class with my custom cell "AccountViewCell. I can get my cell to correctly display in my table, however I run into a problem when I try to link the label in my custom cell to their outlets and when I write to the labels.
Right now my code is:
AccountViewController.h
#import <UIKit/UIKit.h>
#import <GameKit/GKSession.h>
#import <GameKit/GKPeerPickerController.h>
#interface AccountViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
NSArray *tableData;
int cellValue;
NSString *cellContents;
}
#property (nonatomic, retain) NSArray *tableData;
#property (nonatomic, retain) NSString *cellContents;
#property (nonatomic, retain) NSString *accountSelected;
#property (nonatomic, retain) NSString *accountList;
#property (nonatomic, retain) NSString *amount;
#end
AccountViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"AccountViewCell";
AccountViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"AccountViewCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
}
// cell.accountNameLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
AccountViewCell.h:
#import <UIKit/UIKit.h>
#interface AccountViewCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *accountNameLabel;
#property (nonatomic, weak) IBOutlet UILabel *accountNumberLabel;
#property (nonatomic, weak) IBOutlet UILabel *balanceLabel;
#end
AccountViewCell.m
#import "AccountViewCell.h"
#implementation AccountViewCell
#synthesize accountNameLabel = _accountNameLabel;
#synthesize accountNumberLabel = _accountNumberLabel;
#synthesize balanceLabel = _balanceLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
Again, my code works fine and displays my custom cell when I don't try to set a label's text or connect the outlets to the labels in AccountViewCell as shown here:
http://i.imgur.com/TuFun5y.png
My cell Identifier is correct: http://i.imgur.com/ZbsDvaa.png
Now, the problem is when I connect the outlets like this: http://imgur.com/OOiKpkM
The code will break, even if I don't set or declare the labels in AccountViewController.m It gives me this error:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AccountViewController 0x104b2220> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key accountNameLabel.'
*** First throw call stack:
(0x248e012 0x1d2be7e 0x2516fb1 0x815711 0x796ec8 0x7969b7 0x7c1428 0xd620cc 0x1d3f663 0x248945a 0xd60bcf 0xd6298d 0x28e54 0xbfff4b 0xc0001f 0xbe880b 0xbf919b 0xb9592d 0x1d3f6b0 0x287ffc0 0x287433c 0x2874150 0x27f20bc 0x27f3227 0x27f38e2 0x2456afe 0x2456a3d 0x24347c2 0x2433f44 0x2433e1b 0x29e37e3 0x29e3668 0xb4565c 0x49b2 0x2c45)
libc++abi.dylib: terminate called throwing an exception
I need a way to be able to write to the label. I've tried everything I could think of and read many threads, however I cannot figure this out. If someone could please help and could show me a way to write to a label in a custom cell, that would be great.
Ok, I figured it out. I should not have been linking the labels via file owner but instead via the table cell.
I have created a custom cell with its own .m, .h and .xib file. In the cell, I have a UIButton that I added to the xib in IB.
I can receive the IBAction from the UIButton in this custom cell's .m, but really, I'd like to be forwarding that button press to the main view .m that is hosting the table (and so custom cell) and use an action there.
I've spent the last 4 hours attempting various ways of doing this - should I be using NSNotificationCenter? (I've tried Notifications lots but can't get it to work and not sure if i should be persevering)
You need to use delegate in .h file of cell.
Declare the delegate like this
#class MyCustomCell;
#protocol MyCustomCellDelegate
- (void) customCell:(MyCustomCell *)cell button1Pressed:(UIButton *)btn;
#end
then declare field and property
#interface MyCustomCell:UItableViewCell {
id<MyCustomCellDelegate> delegate;
}
#property (nonatomic, assign) id<MyCustomCellDelegate> delegate;
#end
in .m file
#synthesize delegate;
and in button method
- (void) buttonPressed {
if (delegate && [delegate respondToSelector:#selector(customCell: button1Pressed:)]) {
[delegate customCell:self button1Pressed:button];
}
}
Your view controller must adopt this protocol like this
.h file
#import "MyCustomCell.h"
#interface MyViewController:UIViewController <MyCustomCellDelegate>
.....
.....
#end
in .m file in cellForRow: method you need add property delegate to cell
cell.delegate = self;
and finally you implement the method from protocol
- (void) customCell:(MyCustomCell *)cell button1Pressed:(UIButton *)btn {
}
Sorry for my english, and code. Wrote it from my PC without XCODE
I had the same problem, and I solved it by subclassing the cell into it's own class, and put the buttons there as outlets, and filling the cell with data from the model, while using a method that returns the cell we're currently viewing.
For example, if you had a Person class, and each person had a name, a surname, and a number of friends. And each time you clicked a button in the cell, the number of friends for a specific person would increase by 1.
_______________DATA SOURCE___________________________
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *comment;
#property (nonatomic) NSInteger numberOfFriends;
+(instancetype)personWithName:(NSString *)aName Surname:(NSString *)aSurname;
#end
#import "Person.h"
#implementation Person
+(instancetype)personWithName:(NSString *)aName Surname:(NSString *)aSurname{
Person *person = [[Person alloc] init];
[person setName:aName];
[person setSurname:aSurname];
[person setNumberOfFriends:0];
return person;
}
#end
_____________________PERSON CELL________________________
#import <UIKit/UIKit.h>
#interface PersonCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *friendsNum;
#property (strong, nonatomic) IBOutlet UIButton *friendsBtn;
#property (strong, nonatomic) IBOutlet UILabel *nameLabel;
#property (strong, nonatomic) IBOutlet UILabel *surnameLabel;
#end
Personally I created a private NSArray to hold the names of my Person objects, and a private NSMutableDictionary to hold my Person objects, and I set the keys to be the names of the people.
_____________________PERSON TABLE VIEW________________________
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
PersonCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *name = [peopleNames objectAtIndex:indexPath.row];
Person *person = [people objectForKey:name];
if(cell == nil)
{
cell = [[PersonCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.nameLabel.text = person.name;
cell.surname.Label.text = person.surname
[cell.friendsButton addTarget:self action:#selector(moreFriends:) forControlEvents:UIControlEventTouchUpInside];
cell.friendsNum.text = [NSString stringWithFormat:#"%i", person.numberOfFriends];
return cell;
}
- (IBAction)moreFriends:(id)sender {
UIButton *btn = (UIButton *)sender;
PersonCell *cell = [self parentCellForView:btn];
Person *person = [people objectForKey:cell.nameLabel.text];
person.numberOfFriends++;
[self.tableView reloadData];
}
-(PersonCell *)parentCellForView:(id)theView
{
id viewSuperView = [theView superview];
while (viewSuperView != nil) {
if ([viewSuperView isKindOfClass:[PersonCell class]]) {
return (PersonCell *)viewSuperView;
}
else {
viewSuperView = [viewSuperView superview];
}
}
return nil;
}
I would recommend using a delegate.
You might want to just add a selector for the button in your view controller which has your tableview.
in your cellForIndexPath function
[yourCell.button addTarget:self action:#selector(customActionPressed:) forControlEvents:UIControlEventTouchDown];
then handle the button press in your "customActionPressed:(id) sender" method
//Get the superview from this button which will be our cell
UITableViewCell *owningCell = (UITableViewCell*)[sender superview];
//From the cell get its index path.
NSIndexPath *pathToCell = [myTableView indexPathForCell:owningCell];
//Do something with our path
This may be a better solution for you unless there are more factors that you haven't listed.
I have a tutorial which is might explain more http://www.roostersoftstudios.com/2011/05/01/iphone-custom-button-within-a-uitableviewcell/
Why not create a delegate (using #protocol) for your custom cell. You can then designate the main view as each cell's delegate and process the action appropriately.
I'm populating a UITableView with CustomCells and I'm trying to get didSelectRowAtIndexPath called. Here is the header for the Custom Cell.
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell {
IBOutlet UILabel *bizNameLabel;
IBOutlet UILabel *addressLabel;
IBOutlet UILabel *mileageLabel;
IBOutlet UIImageView *bizImage;
}
#property (nonatomic, retain) UILabel *bizNameLabel;
#property (nonatomic, retain) UILabel *addressLabel;
#property (nonatomic, retain) UILabel *mileageLabel;
#property (nonatomic, retain) UIImageView *bizImage;
#end
Pretty simple and straightforward. I have a detailDisclosureButton I'm adding to the cell as well in the cellForRowAtIndexPath method in the cell as well, and the method accessoryButtonTappedForRowWithIndexPath: is being called, but didSelectRowAtIndexPath is not.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellView"
owner:self options:nil];
#ifdef __IPHONE_2_1
cell = (CustomCell *)[nib objectAtIndex:0];
#else
cell = (CustomCell *)[nib objectAtIndex:1];
#endif
}
// Configure the cell.
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
/*
CODE TO POPULATE INFORMATION IN CUSTOM CELLS HERE
*/
#ifdef __IPHONE_3_0
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
#endif
return cell;
}
I put an NSLog inside all the methods as well as break points. The method I'm trying to get called is not, but inside my CustomCell class, the following method is. So is there a way to get didSelectRowAtIndexPath to get called while using a CustomCell?
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
Custom cell or not should not make any difference. Are you in editing mode? If you are, you have to set allowsSelectionDuringEditing = true on the tableView.
if you are use Tableview in your xib..so u want to give tableview's data source and delegate connection in file owner..
Since you say that tableView:accessoryButtonTappedForRowWithIndexPath: is getting called, we know that your tableView's delegate property is properly set. So tableView:didSelectRowAtIndexPath: should be getting called as well. If it isn't getting called, my best guess is that you have a typo somewhere in the method signature. Copy and paste this method signature into your code to make sure you didn't accidentally omit the "tableView:" part or make a capitalization error.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
EDIT: Secondary hypothesis: In the .xib where you've defined your custom cell, make sure "User Interaction Enabled" is checked.
check if you have a UITapGestureRecognizer set for myTableView's parent view ..that is probably over riding the touch event and consuming it.
been confused about this for over two hours, so maybe someone can point me at the right direction...
i have a navigation bar with a tableViewController under it. once the first row is selected i am pushing a new tableViewController that loads up custom table cells with the new and shiny UINib object.
cellForRowAtIndexPath is called and i allocated a new row, set up the values of it's two UILabel correctly, and return the cell.
however - the table view is completely empty. if i replace the custom cell with a regular table cell, i see the cell. what the hell is going on here?
some code:
in viewdidLoad:
self.cellNib = [UINib nibWithNibName:#"DetailCell" bundle:nil];
in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"detailCell";
DetailCell* cell = (DetailCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[self.cellNib instantiateWithOwner:self options:nil];
cell = tmpCell;
self.tmpCell = nil;
}
cell.fieldName.text = #"field title";
cell.fieldValue.text = #"field value";
return cell;
}
and the custom cell (that has a xib file associated with it as well):
#interface DetailCell : UITableViewCell {
IBOutlet UILabel* fieldName;
IBOutlet UILabel* fieldValue;
}
#property (nonatomic, retain) IBOutlet UILabel* fieldName;
#property (nonatomic, retain) IBOutlet UILabel* fieldValue;
#end
thanks for your help.
for anyone following this thread, i found the issue to be with a missing cell identifier. the value you define at rowAtIndex needs to be entered using IB for the xib file. there is an identifier field.
I wrote a Special UITableViewCell. Now I need to get the text out of a textfield in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *custumCell = [tableView cellForRowAtIndexPath:indexPath];
//warning: incompatible Objective-C types initializing 'struct UITableViewCell *', expected 'struct CustomCell *'
NSString *title = [custumCell cellTitle].text;
[self setArticleReaded:title];
[title release];
}
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell {
IBOutlet UILabel *cellTitle;
IBOutlet UILabel *cellSubTitle;
IBOutlet UILabel *cellPubDate;
}
#property (nonatomic, retain) IBOutlet UILabel *cellTitle;
#property (nonatomic, retain) IBOutlet UILabel *cellSubTitle;
#property (nonatomic, retain) IBOutlet UILabel *cellPubDate;
#end
I hope there is anybody who knows a workaround.
Cast it:
CustomCell *custumCell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
This should give you access to the cell's properties.
You should cast to your CustomCell type:
CustomCell *custumCell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
I don't see any point in calling the cellForRowAtIndexPath, that builds the cell out of the data source (e.g. NSArray) and fills the cellText with text. And then access to the cellText property.
Just pull the needed text directly from the NSArray...