I am using the following 2 methods to return a custom cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *key = [self keyForIndexPath:indexPath];
UITableViewCell *cell;
if ([key isEqualToString:DoneButtonCellKey]) {
cell = [self [self doneButtonCellForIndexPath:indexPath];
return cell;
} else {
//code to return default cell...
}
}
Then:
- (DoneButtonCell *)doneButtonCellForIndexPath: (NSIndexPath *)indexPath {
DoneButtonCell *cell = [self.tableView dequeueReusableCellWithIdentifier:DoneButtonCellIdentifier forIndexPath:indexPath];
return cell;
}
What is the correct init method to use with the cell here so I can change some properties of the cell when it is initialized?
EDIT: I found the problem, as the init/awakeFromNib methods were not being called for me. I tracked down the error and it was that I had not changed the "Custom Class" from UITableViewCell to my custom class. Now awakeFromNib AND initWithCoder work as described below.
You can do your changes inside the DoneButtonCell's class, either in the
- (void)awakeFromNib
{
.. essential to call super ..
super.awakeFromNib()
//Changes done directly here, we have an object
}
Or the initWithCoder: method:
-(id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
//Changes here after init'ing self
}
return self;
}
If you're using Swift, remember the easy way to ensure a view is initialized when it is created is to use the didSet method. For example, to make a UIImageView into a round shape you could add code like this:
#IBOutlet weak var profileImageView: UIImageView! {
didSet {
// Make the profile icon circle.
profileImageView.layer.cornerRadius = self.profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true
}
}
This is how I am initialising custom cells
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"FileTableViewCell";
FileTableViewCell *cell = (FileTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"FileTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// Configure the cell here...
// Configure the cell.
FileRepresentation* fileRepresentation = _fileList[indexPath.row];
cell.textLabel.text = [self userFilename:[fileRepresentation.fileName stringByDeletingPathExtension]];
cell.detailTextLabel.text = [fileRepresentation modifiedDate];
cell.accessoryView=nil;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[cell.progressIndicator setHidden:YES];
cell.imageView.image = [UIImage imageNamed:_fileImageName];
// Disable any user interaction while processing a request
if (_fileIsOpen || _creatingDocument || _deletingDocument) {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.textColor = [UIColor grayColor];
} else {
cell.textLabel.textColor = [UIColor blackColor];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
}
First try to dequeue a cell if possible using dequeueReusableCellWithIdentifier method of UITableView.
If cell is not available (nil) use [[NSBundle mainBundle] loadNibNamed:#"<#your custom cell nib name#>" owner:nil options:nil][0] to initialize it.
In your custom cell's .m file, implement initWithCoder: initializer for custom initialization code:
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
//your custom initialization code
return self;
}
This is the designated initializer that is called when any view is loaded from a nib with loadNibNamed, like a custom table view cell.
Related
I Have a issue regarding accessing cell label outside of cellForRowAtIndexPath. Firstly in cellForRowAtIndexPath i am hiding cell.lblempName.hidden = YES;
Below is my code :
- (void)viewDidLoad {
[super viewDidLoad];
self.tblemplist.tableFooterView=self.footerView;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
EmployeeCell *cell = (EmployeeCell *) [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"EmployeeCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (EmployeeCell *) currentObject;
cell.backgroundColor=[UIColor clearColor];
}
}
}
NSMutableDictionary *detailsdict=[empArray objectAtIndex:indexPath.row];
cell.lblTitle.text = [detailsdict objectForKey:#"empTitle"];
cell.lblItemprice.text = [detailsdict objectForKey:#"empId"];
cell.lblempName.hidden = YES;
}
And in the Footer i have added a View and placed a button. When i click the button lblempName should UnHide.
- (IBAction)btnApplyClicked:(id)sender {
// How to unHide cell.lblempName.hidden = NO;
}
please help me to find the solution. TIA
create a bool property as BOOL isEmpNameHidden;
In viewDidLoad, set isEmpNameHidden = YES;
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Your existing code here
....
cell.lblempName.hidden = isEmpNameHidden;
}
then unhide based on the button tap
- (IBAction)btnApplyClicked:(id)sender {
isEmpNameHidden = NO;
[self.tblemplist reloadData]
}
EmployeeCell *cell = (EmployeeCell *)[self.tableView cellForRowAtIndexPath:indexPath];
UILabel *label = cell.lblempName;
label.hidden = NO;
But better is to modify your data, because as soon as the cell scrolls out of the view and back again the label will be visible again since it will be constructed again.
I have a UITableView with two different custom table cells. The first cell appears normal after I start the app. The second cell will appear when you click on them.
Can anybody help me or has an idea?
Thanks a lot.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"customCell2";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor grayColor];
cell.textLabel.font = [UIFont fontWithName:#"STHeitiSC-Light" size:9.0];
}
return cell;
}
Having done custom UITableViewCell in the past I usually handle the nib loading in the custom class itself.
The basic header for the custom cell.
#interface RequestsTableViewCell : UITableViewCell {
// Ivars.
}
// Properties.
- (id) initWithRequestModel: (RequestModel *) model style:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier forQueryType:(int) requestType;
// Other methods, etc.
#end
The custom cell with a designated initializer.
#implementation RequestsTableViewCell
- (id) initWithRequestModel: (RequestModel *) model style:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier forQueryType:(int) requestType {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"RequestsTableViewCell" owner:self options:nil];
self = [nibArray objectAtIndex:0];
requestModel = model;
queryType = requestType;
[self setRequestThumbnail];
[self setRequestCategory];
[self setRequestAddress];
[self setRequestStatusDate];
[self setRequestStatus];
[self setRequestFollowed];
[self setRequestComment];
[self setAppearance];
}
return self;
}
There would also be a custom xib for the custom UITableViewCell that corresponds and has the custom class set in the identity inspector.
In the UITableViewController.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"Cell Id";
RequestModel *request = nil;
// Other code for search, etc
request = [self.serviceRequests objectAtIndex:indexPath.row];
RequestsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if(!cell) {
cell = [[RequestsTableViewCell alloc] initWithRequestModel:request style:UITableViewCellStyleDefault reuseIdentifier:cellId forQueryType:queryTypeIndicator];
}
return cell;
}
It also sounds like you have more than one custom cell type in your question? Can you elaborate on how it is all supposed to function? You say that you have to click one cell to make another appear, can you explain that interaction?
I did something similar, but made the cell 'expand', instead of adding a new cell. Of course then you don't have two cells, but you can resize your one cell, add subframes,...
You can keep a boolean in your UITableViewCell object (BOOL cellIsExpanded), and set that on tap gesture. Then in drawRect of the TableViewCell, layout your cell accordingly.
Example code, on expand, make cell height 20-->80 and add a UIButton:
In the TableViewController, overload heightForRowAtIndexPath (this will resize your cell if 'expanded'):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
YourEntity *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (!record.cellIsExpanded)
return 20.; // cell is smaller if collapsed
else
return 80.; // bigger cell
}
In the TableViewCell, add or remove subframes:
#interface MyTableViewCell ()
#property(nonatomic) BOOL cellIsExpanded
#property(strong, nonatomic) UITextField *myTextField;
#property(strong, nonatomic) UIButton *clickMeButton;
#end
#implementation MyTableViewCell
- (void)drawRect:(CGRect)rect {
if(!self.cellIsExpanded){
// layout your collapsed cell, for example:
self.myTextField = [[UITextField alloc] initWithFrame:self.frame];
self.myTextField.text = #"Collapsed cell";
// remove button, only present in expanded view :
self.clickMeButton=nil;
}
else{
self.myTextField.text = #"Expanded cell";
// add button below textfield
self.clickMeButton = [[UIButton alloc] initWithFrame:CGRectMake(20, 20, 10, 10)];
}
}
#end
Trying to get my app to display text in its cells. Been scratching my head for a while. This is the code used to display the text but nothing appears. Any advice?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"leftMenuCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if (indexPath.section == 0) {
NSArray *titles = #[#"Quick Glance", #"Your Home", #"Invites"];
cell.textLabel.text = titles[indexPath.row];
}
cell.backgroundColor = [UIColor clearColor];
return cell;
}
If you have not add the following code in ViewDidLoad() method, please do so.
self.tableView.dataSource = self;
self.tableView.delegate = self;
Firstly you should break this out differently so that you are not re-creating your backing store every time you are producing a cell.
More code would be required in order to ascertain exactly what the issue but at minimum you could put the following in to verify this.
#implementation ViewController {
NSArray *_titles;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_titles = #[#"Quick Glance", #"Your Home", #"Invites"];
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [_titles count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"leftMenuCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = titles[indexPath.row];
cell.backgroundColor = [UIColor clearColor];
return cell;
}
This is the bare minimum you will need above in order to show some information in the tableview
Now the question is are you using a UITableViewController where it already has set the dataSource and delegate methods or are you using a UIViewController where the dataSource and delegate have not been set (either in code or through XIB or Storyboard)?
If you do not have the delegates set up you need to set them up either through the storyboard or through the code, and if through code and using UIViewController make sure you have a reference to the UITableView otherwise you will not be able to set the datasource.
I currently have a tableView with custom cells that I've called AMPFeedbackTableViewCell. The cells load up perfectly fine.
Here is the code:
-(UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"feedbackCell"];
AMPFeedbackTableViewCell* cell = (AMPFeedbackTableViewCell*) [tableView dequeueReusableCellWithIdentifier:#"feedbackCell"];
if (cell == nil)
{
cell = [[AMPFeedbackTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"feedbackCell"];
cell.currentFeedback = [[AMPFeedback alloc] init];
cell.currentFeedback = [_feedbackArray objectAtIndex:indexPath.row];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"FeedbackTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
I'm trying to pass something to the cell before it starts like in this case it's _currentFeedback. The custom cell has a AMPFeedback item and I want it to set it before it loads. So I can use it like this : (NOTE: This is in AMPFeedbackTableViewCell
- (void)awakeFromNib
{
// Initialization code
if (_currentFeedback != nil)
[self loadImages];
}
However, _currentFeedback is always nil. Is there a way I can pass it over then call awakeFromNib?
Thanks in advance
If you are not insist on doing this inside awakefromNib, there's another way (and maybe better) to do this:
in AMPFeedbackTableViewCell.h
#property (strong) AMPFeedBack *currentFeedback;
in AMPFeedbackTableViewCell.m
#synthesize currentFeedback = _currentFeedback;
- (void)setCurrentFeedback:(AMPFeedback *)feedback {
if (feedback==nil) return;
_currentFeedback = feedback;
[self loadImages];
}
and then we can trigger it directly in main code:
-(UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
AMPFeedbackTableViewCell* cell = (AMPFeedbackTableViewCell*) [tableView dequeueReusableCellWithIdentifier:#"feedbackCell"];
if (cell == nil) {
cell = [[AMPFeedbackTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"feedbackCell"];
}
cell.currentFeedback = [_feedbackArray objectAtIndex:indexPath.row];
Hope it helps.
You can do initialization and loading of data in the following method of custom uitableview cell
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
and you can load customcell in your tableview
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *eventListCell = #"EventListCell";
EventListCell *cell = (EventListCell *)[tableView dequeueReusableCellWithIdentifier:eventListCell];
if (cell == nil)
{
cell = (EventListCell*)[[[NSBundle mainBundle] loadNibNamed:#"EventListCell" owner:self options:nil] objectAtIndex:0];
// here you can access the custom class public variable to set data .
}
}
This works perfect if I return just 1 row of cells.
But if I put more than 1 I get the error message: "index 1 beyond bounds [0 .. 0]"
I have made a xib file that has one table cell with a unique design and I just want to use that over and over only changing some labels. Am I going about this the wrong way?
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (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 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
SectionCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:#"ButtonCell" owner:self options:nil];
cell = [[SectionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
cell = (SectionCell *)[nib objectAtIndex:0];
}
cell.backgroundView.layer.masksToBounds = YES;
cell.backgroundView.layer.cornerRadius = 20.0;
cell.name.text=#"Cell Test";
tableView.backgroundView = nil;
tableView.backgroundColor = [UIColor clearColor];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
Change to this code snipped:
if (cell == nil)
cell = [[[SectionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier] autorelease];
and remove your NSBundle logic as it is no longer necessary since you are instantiating the cell object yourself.
Example:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
SectionCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
cell = [[[SectionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier ] autorelease];
cell.backgroundView.layer.masksToBounds = YES;
cell.backgroundView.layer.cornerRadius = 20.0;
cell.name.text=#"Cell Test";
tableView.backgroundView = nil;
tableView.backgroundColor = [UIColor clearColor];
return cell;
}
Tip:
It isn't necessary to continuously set your tableview backgrounds the way you do. You can set it when your object is first loaded.
Instead of using :
cell = [nib objectAtIndex:indexPath.row];
use:
if (cell == nil) {
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:#"ButtonCell" owner:self options:nil];
cell = (SectionCell *)[nib objectAtIndex:0];
}
Because your nib probably has only one object i.e your custom table view cell.
PS: Assuming you have a xib named "ButtonCell" having a single custom view with its custom class set to "SectionCell"
Found the answer. I had to not use static for the table view and use dynamic prototype. The only downside is I couldn't use the custom xib cell and had to modify the default cell to look as close as possible to my static cell.