So I've used this tutorial to populate a UITableView with custom cells that represent balances. When stepping through the code, I witness the correct amount of cells get created (only 4 with the current test data) and their labels' text set correspondingly.
My problem is when the table is displayed on the screen, only the first row/cell is displayed.
Any insight as to why this could be occurring would be greatly appreciated!
Removed old code.
BalanceCell.h:
#import <UIKit/UIKit.h>
#interface BalanceCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#property (weak, nonatomic) IBOutlet UILabel *amountLabel;
#property (weak, nonatomic) IBOutlet UILabel *modifiedLabel;
#end
EDIT:
My TableView delegate methods are now as follows:
- (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 [_balances count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
BalanceCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[BalanceCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [_hex colorWithHexString:_themeColourString];
return cell;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(BalanceCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
Balance *item = [_balances objectAtIndex:indexPath.row];
cell.nameLabel.textColor = _themeColour;
cell.nameLabel.text = item.name;
cell.amountLabel.textColor = _themeColour;
cell.amountLabel.text = [NSString stringWithFormat:#"%#%#", item.symbol, item.value];
cell.modifiedLabel.textColor = _themeColour;
cell.modifiedLabel.text = [NSString stringWithFormat:#"%#", item.modified];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 94;
}
As #Sebyddd suggested, I now register the NIB in the viewDidLoad method.
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:#"BalanceCell" bundle:nil] forCellReuseIdentifier:#"Cell"];
}
These changes may make my code more correct but still only the first cell is displayed.
If cells are getting created and returned properly I guess height is not being set propery. By default I beleive all cells have a height of 44. If your cell exceeds this height it might not get displayed.
You can tell the tableview to adjust height for every cell using (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath delegate
In that delegate just return your cells height.
EDIT:
You are using dequeueReusableCellWithIdentifier: which will return A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
Instead use dequeueReusableCellWithIdentifier:forIndexPath: which will return A UITableViewCell object with the associated reuse identifier. This method always returns a valid cell.
You need to register the nib/class for that custom cell in viewDidLoad
Try this:
if (cell == nil) {
[tableView registerNib:[UINib nibWithNibName:#"BalanceCell" bundle:nil] forCellReuseIdentifier:#"Cell"];
cell = [[BalanceCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
Use this tuto : http://www.appcoda.com/uitableview-tutorial-storyboard-xcode5/ , your tuto is a bit outdated, and hard to follow !
Related
I'm currently creating my UITableViewCells programmatically like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
UILabel *newLabel = [[UILabel alloc] initwithframe:cell.frame];
[newLabel setText:self.data[indexPath.row]];
[cell addSubview:newLabel];
return cell;
}
This seems to create a new UILabel each time the cell is reused though, which I definitely don't want. I tried doing the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
if (cell == nil) {
UILabel *newLabel = [[UILabel alloc] initwithframe:cell.frame];
[cell addSubview:newLabel];
}
[newLabel setText:self.data[indexPath.row]];
return cell;
}
but then the UILabel seems to never be created. Perhaps this is because I'm using prototype cells with Storyboard and thus the cells are never nil?
You have two solutions.
Create a custom table view cell that already has the label.
If you want to add the label in code, don't register a class for the cell. Then the dequeued cell can be nil and you can add the label at that time (like in your 2nd set of code). This also requires using the dequeueReusableCellWithIdentifier method that doesn't also take an indexPath.
You should create a UITableViewCell subclass and add "newLabel" as a property.
The cell is never nil because the method you use to dequeue the table view cell always returns a cell, creating one if it doesn't already exist in the reuse queue.
A better solution would be to create the label in the cell prototype in the storyboard.
This implementation is against MVC architecture where controller managers stuff and do not deal with view. Here, you are trying to add stuff in view from controller. It is suggested to subclass UITableViewCell as below and add your custom UI controls in there
MyTableViewCell.h
#interface MyTableViewCell : UITableViewCell {
}
#property (nonatomic, strong) UILabel *myLabel;
#end
Then you can implement layoutSubviews in your MyTableViewCell.m file to define the look and feel of your cell.
MyTableViewCell.m
- (id)initWithStyle:(UITableViewCellStyle)iStyle reuseIdentifier:(NSString *)iReuseIdentifier {
if ((self = [super initWithStyle:iStyle reuseIdentifier:iReuseIdentifier])) {
self.myLabel = [[UILabel alloc] initWithFrame:<Your_Frame>];
// Set more Label Properties
[self.contentView addSubview:self.myLabel];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
// Override run time properties
}
Finally use your custom cell like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Home-Cell"];
}
cell.myLabel.text = self.data[indexPath.row];
return cell;
}
As a side note, I hope you know that you get textLabel and detailTextLabel free from default UITableViewCell implementation.
I have created a UITableView in Storyboard and it is dynamically cell. The problem is that when there are not enough cells to reuse, it randomly empties a few of my cells. I think this is logical, but I want to resolve this problem.
I give an example:
I have a UITableView that is capable to generate 10 cells in a view. But now, I only want to show 8 cells out of 10. It gives no problem when I have only a section. With more than 1 section, it will always empty 2 cells and show 8 cells, but it should show 10 cells.
Can anyone help me with this? Thank you.
Updated With Source code
#pragma mark - List View
#pragma mark - Table View Delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section {
// Number of rows is the number of time zones in the region for the specified section.
return self.listCollection.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ListCell" forIndexPath:indexPath];
for (id object in cell.contentView.subviews) {
[object removeFromSuperview];
}
[cell.contentView addSubview:[self.listCollection objectAtIndex:indexPath.row]];
return cell;
}
self.listCollection has an array of UIView Object.
Updated With Images:
Image 1
Image 2
It is happening because you are using 2 sections but are not specifying the content for each section separately. To understand this we need to look into the description of addSubview: method from Apple Documentation
This method establishes a strong reference to view and sets its next responder to the receiver, which is its new superview.
Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview.
Have a good look at bold section in second paragraph. As you are using the same view object from listCollection to populate both the section, so newest created cell will become the superview for this view object and previous cell will be left out with nothing but the plane contentView. You can get the real feel by assigning some default color to the cell contentView. Your blank cell will be displaying the color in full content while other cells will display the view
cell.contentView.backgroundColor = [UIColor greenColor];
Solution
I will recommend to use 2 different datasource for both sections as explained below.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section {
// Number of rows is the number of time zones in the region for the specified section.
if(section==0)
return self.listCollection.count;
else
return <row count for section 1>
}
Same way you need to modify the cellForRowAtIndexPath: code for each section.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ListCell" forIndexPath:indexPath];
for (id object in cell.contentView.subviews) {
[object removeFromSuperview];
}
if(section==0)
[cell.contentView addSubview:[self.listCollection objectAtIndex:indexPath.row]];
else
[cell.contentView addSubview:[<second array of views> objectAtIndex:indexPath.row]];
return cell;
}
But if that is your case that you have to use same array, then you can use this technique. Thanks to Rishi for that.
Last but not least, you should use a custom UITableViewCell class as Michal Zygar has suggested in his post.
UIView *tempView = [self.listCollection objectAtIndex:indexPath.row];
NSData *tempArchiveView = [NSKeyedArchiver archivedDataWithRootObject:tempView];
UIView *viewOfSelf = [NSKeyedUnarchiver unarchiveObjectWithData:tempArchiveView];
[cell.contentView addSubview:viewOfSelf];
You are using the UITableView wrong way.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ListCell" forIndexPath:indexPath];
for (id object in cell.contentView.subviews) {
[object removeFromSuperview];
}
[cell.contentView addSubview:[self.listCollection objectAtIndex:indexPath.row]];
return cell;
}
This is highly inapriopriate. Your listCollection should contain model data. In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath you should configure the cell with this data.
So you should subclass the UITableViewCell for example
#interface ApartmentCell:UITableViewCell
#property (weak, nonatomic) UILabel* floor;
#property (weak, nonatomic) UILabel* unit;
#property (weak, nonatomic) UILabel* type;
#property (weak, nonatomic) UILabel* area;
#property (weak, nonatomic) UILabel* price;
#end
and then
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ApartmentCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ListCell" forIndexPath:indexPath];
Apartment* apartment=self.listCollection[indexPath.row];
cell.unit.text=apartment.unit;
//and so on with other fields
return cell;
}
I am beginning with iOS have a silly problem. I just want to show a TableView populated with strings that are stored in a NSMutableArray. I can see that the strings are in the array, but for some reason the TableView is not showing them.
I have bascially this:
#interface Test ()
#property (weak, nonatomic) IBOutlet UITableView *contactList;
#property (strong, nonatomic) NSMutableArray *contactsArray;
#end
- (void)onContactFound:(NSString*)contact
{
[self.contactsArray addObject:contact];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.contactsArray count];
}
//4
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//5
static NSString *cellIdentifier = #"SettingsCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
//6
NSString *tweet = [self.contactsArray objectAtIndex:indexPath.row];
//7
[cell.textLabel setText:tweet];
[cell.detailTextLabel setText:#"via Codigator"];
return cell;
}
I think the problem is in the last part. I copied this code from an example (http://www.codigator.com/tutorials/ios-uitableview-tutorial-for-beginners-part-1/) that said I should add some dynamic properties but in my TableView I do not have these properties in the attributes inspector so basically I do not have the #"SettingsCell" so I guess this is one of the problems at least, maybe this code does not apply in my case and it should be done in another way?
I think you are trying to dequeue a cell without ever creating cells. I think you only get nil cells back. You should use something like this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
Also have a look in the API documentation which states:
dequeueReusableCellWithIdentifier: Returns a reusable table-view cell object located by its identifier.
Return Value: A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
dequeueReusableCellWithIdentifier:
I want to use a custom UITableviewCell with UITableView in same xib without creating a UITableViewCell class?
As you can see bellow i set the identifier for UITableViewCell and used it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
}
But Not Working?
Add a UITableViewCell *customCell property to your view controller (for example, your file ShowUsers.h)...
#property (nonatomic, strong) IBOutlet UITableViewCell *customCell;
and connect it to the custom cell in your xib. Then use the following code in cellForRow (for example, your file ShowUsers.m) :
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NibName" owner:self options:nil];
cell = self.customCell;
self.customCell = nil;
}
Yes you can do as following code
Under ViewDidLoad or ViewWillAppear
label1Array=[NSMutableArray arrayWithObjects:#"A",#"B",nil];
label2Array=[NSMutableArray arrayWithObjects:#"C",#"D",nil];
label3Array=[NSMutableArray arrayWithObjects:#"E",#"F",nil];
UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"aCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *label1=[[UILabel alloc]init];
label1.frame=CGRectMake(0,0,40,40);
label1.text=[label1Array objectAtIndex:indexPath.row];
................
[cell.contentView addSubView: label1];
UILabel *label2=[[UILabel alloc]init];
label2.frame=CGRectMake(50,0,40,40);
label2.text=[label2Array objectAtIndex:indexPath.row];
[cell.contentView addSubView: label2];
UILabel *label3=[[UILabel alloc]init];
label3.frame=CGRectMake(100,0,40,40);
label3.text=[label3Array objectAtIndex:indexPath.row];
[cell.contentView addSubView: label3];
}
Hope this Helps !!!
See..You can do like this. Here you are adding two UITableViewCell in one TableView using XIB.
In Class.h file :-
Declare one mutable array for number of cells.
{
NSMutableArray * sectionRows;
}
#property (nonatomic,retain) IBOutlet UITableViewCell *addTvc1;
#property (nonatomic,retain) IBOutlet UITableViewCell *addTvc2;
In Class.m :-
#synthesize addTvc1, addTvc2;
- (void)viewDidLoad
{
[super viewDidLoad];
sectionRows = [[NSMutableArray alloc] init];
[sectionRows addObject:[[NSMutableArray alloc]
initWithObjects:addTvc1, nil]];
[sectionRows addObject:[[NSMutableArray alloc]
initWithObjects:addTvc2, nil]];
}
In UITableViewDelegate Method -
Add This -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sectionRows count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
return [[sectionRows objectAtIndex:section] count];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
return [[sectionRows objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];
}
Along with code, In Class's XIB drop and drag two UITableViewCell which should be outside from the view and add those cells with Files Owner.
It will work perfectly. Here no need to create one separate custom UITableViewCell class.
I searched too much, tried lot of thinks but i couldn't make it work.
I have a view controller, and in that controller i have a tableview which has custom cell.
I connect outlets,datasource and delegate but when i run the project, instead of my custom cells. UITableCell is shown and not showing data either. here is my .h and .m files
#import <UIKit/UIKit.h>
#class SelectMutantCell;
#interface MutantViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>{
UITableView *tableView;
IBOutlet SelectMutantCell *mutantCell;
}
#property (nonatomic, weak) IBOutlet UITableView *tableView;
#property (nonatomic, weak) SelectMutantCell *mutantCell;
- (IBAction)cancel:(id)sender;
#end
and here is my .m file
#implementation MutantViewController
#synthesize tableView = _tableView;
#synthesize mutantCell = _mutantCell;
NSArray *mutants;
- (void)viewDidLoad
{
[super viewDidLoad];
//mutants = [mutants initWithObjects:#"Keko",#"HEHE",#"PEPE", nil];
UINib *cellNib = [UINib nibWithNibName:#"SelectMutantCell" bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"SelectMutantCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"SelectMutantCell";
SelectMutantCell *cell = (SelectMutantCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSLog(#"---");
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 3;
}
the NSLog(#"---") is for debugging and it appears when running. the problem is in
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
this block, if i write cell.textLabel.text = "test" it works but i need my custom cell to shown.
any help will be appreciated..
Insert if(cell==nil){} loop
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"SelectMutantCell";
SelectMutantCell *cell = (SelectMutantCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[SelectMutantCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"---");
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
return cell;
}
Looking at your cellForRowAtIndexPath: method, you never initialize the cell. Try doing it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"SelectMutantCell";
SelectMutantCell *cell = (SelectMutantCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"SelectMutantCell" owner:nil options:nil];
for(id currentObject in topLevelObjects) {
if([currentObject isKindOfClass:[SelectMutantCell class]]) {
cell = (SelectMutantCell *)currentObject;
break;
}
}
}
NSLog(#"---");
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
return cell;
}
I had the same issue. To solve it check the followings :
in your nib file, you have removed the root view created by default, and dragged a UITableViewCell from XCode object browser, then set the custom type of the UItableViewCell to your subclass SelectMutantCell
you have linked the outlets
you have added the cell identifier to SelectMutantCell, as you would do in storyboard
last but not least, you didn't add the same cell in your storyboard TableView, it will conflit between the one from storyboard and the one from [self.tableView registerNib:cellNib forCellReuseIdentifier:#"SelectMutantCell"];