I have a custom UITableViewCell in which I have two UIViews . I want to change the BackgroundColor radius and some more properties of UIViews.
But I am unable to do so.
Here is my setUp.
Step 1:
Create A Custom Cell with XIB.
Step 2: Entered the Cell Identifer name in XIB for CustomCell.
Step 3: Instantiated NIB in viewDidLoad
UINib *nib = [UINib nibWithNibName:#"CustomCell" bundle:nil];
[[self mTableView] registerNib:nib forCellReuseIdentifier:#"CustomCell"];
Step 4: Cell For Row At Index Method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of ItemCell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
[cell updateCellView];
return cell;
}
Step 5: Checked Twice the Outlet Connections all are Fine.
Step 6: Custom Cell Class:
#import "TransportCell.h"
#implementation TransportCell
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)updateCellView
{
self.backgroundView.backgroundColor = [UIColor redColor];
}
#end
This code has no effect on my Cell View.
I debug the code. When I logged the backgroundView I get nil when updateCellView method get called:
Here is my CustomCell's Xib:
I have to change the Properties of Inner UIView.(Blue in color)
Instead of calling the method in the cellForRowAtIndexPath try this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of ItemCell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
return cell;
}
and then in your custom method
- (void)updateCellView
{
self.backgroundView.backgroundColor = [UIColor redColor];
// reload the table view
[self.tableView reloadData];
}
you need to add [ tableview reloadData ]
in method update cell view
Ideally all the info needed to render the cell should come from either the data source or other events. For example, if you are changing the border-color it should mean that either some entity (say user) has performed an event or something has changed in the data-source like a value passed its threshold.
The change in data-source or those events should either trigger refreshing the whole table:
-(void) reloadData
or some cells:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Only then the cells will get updated.
you are changing the self.backgroundview.backgroundColor
Default it is nil for cells in UITableViewStylePlain, and non-nil for UITableViewStyleGrouped.
The 'backgroundView' will be added as a subview behind all other views.
#property (nonatomic, strong, nullable) UIView *backgroundView;
try this method in TransportCell, it's swift code make same for objective c
override func layoutSubviews()
{
self.backgroundView.backgroundColor = [UIColor redColor];
}
Use this:
self.contentView.backgroundColor = [UIColor redColor];
Hope this helps!
Related
How do I get tableView by custom cell in the CustomCell?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell" forIndexPath:indexPath];
return cell;
}
#implementation CustomCell
- (void)awakeFromNib {
[super awakeFromNib];
// How do I get tableView by custom cell in the CustomCell?
}
#end
To answer the question, Apple does not provide a public API for that and you would have to do it using what is known about view hierarchies.
A tableViewCell would always be part of a tableView. Technically, a tableViewCell would always be in a tableView's view hierarchy or a tableViewCell would always have some superview out there that is a tableView. Here is a method similar to this one:
- (UITableView *)getParentTableView:(UITableViewCell *)cell {
UIView *parent = cell.superview;
while ( ![parent isKindOfClass:[UITableView class]] && parent.superview){
parent = parent.superview;
}
if ([parent isKindOfClass:[UITableView class]]){
UITableView *tableView = (UITableView *) parent;
return tableView;
} else {
// This should not be reached unless really you do bad practice (like creating the cell with [[UITableView alloc] init])
// This means that the cell is not part of a tableView's view hierarchy
// #throw NSInternalInconsistencyException
return nil;
}
}
More generally, Apple did not provide such public API for a reason. It is indeed best practice for the cell to use other mechanisms to avoid querying the tableView, like using properties that can be configured at runtime by the user of the class in tableView:cellForRowAtIndexPath:.
I have taken over an iOS project and have to refactor a list of views into a UITableView. I am using Storyboards and have subclassed UITableViewCell. One subclass is called MenuItemCell and has a headerLabel, detailLabel, and priceLabel which are properties set up in the Storyboard and configured in MenuItemCell. I am able to manipulate these via cellForAtIndexPath like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MenuItemCellIdentifier=#"MenuItemCell";
id dic=self.tmpMenu.listItems[indexPath.row];
if([dic isKindOfClass:[MenuItem class]]){
MenuItemCell *cell = [self.menuTV dequeueReusableCellWithIdentifier:MenuItemCellIdentifier];
MenuItem *menuItem=(MenuItem *)dic;
cell.menuItem=menuItem;
cell.headerLabel.text=menuItem.header;
cell.headerLabel.numberOfLines=0;
cell.priceLabel.text=menuItem.price;
// how to handle this custom spotView
if([menuItem hasInstoreImage]){
UIView *instoreImageDot=[self circleWithColor:[UIColor redColor] radius:4];
[cell.spotView addSubview:instoreImageDot]; // ON SCROLLING, this populates to all the different table cells
}
return cell;
}
return nil;
}
The last piece is that there is a custom UIView called spotView. Currently, I am creating this circle in code in my controller via circleWithColor and trying to add to [cell.spotView] but scrolling causes this to populate on different table cells. How should I set this up? I have added a method to my custom view but this suffers from the same problem.
Cells get reused, you will need to tell the tableView to remove the custom View
if([menuItem hasInstoreImage]){
UIView *instoreImageDot=[self circleWithColor:[UIColor redColor] radius:4];
[cell.spotView addSubview:instoreImageDot];
}else{
//remove it if condition is not met
//or You can add a place holder view instead
}
What is happening is that iOS is reusing cells as you scroll and some of the reused cells already have the instoreImageDot view added as a subview.
You really shouldn't do layout stuff in the cellForRowAtIndexPath method. It should only ever be used to dequeue a reusable cell and then set the data for the cell. All the layout stuff should be handled by the cell itself.
Don't create the instoreImageDot in the controller. Add a method in your custom cell - something like (written in C#, but should be easy to translate):
UpdateCell(MenuItem item, bool hasInstoreIamge)
{
menuItem = item;
headerLabel.text = item.header;
priceLabel.text = item.price;
headerLabel.numberOfLines=0;
if (hasInstoreImage)
{
// code to add the instoreImageDot as a subview of the cell
}
}
Also in the Custom Cell, Implement the prepareForReuse method and inside this method, remove the instoreImageDot view from the cell - so that it can only ever be added once.
- (void)prepareForReuse {
if([self.subviews containsObject:instoreImageDot])
{
[instoreImageDot removeFromSuperview];
}
[super prepareForReuse];
}
Now your cellForRowAtIndexPath method can look like:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MenuItemCellIdentifier=#"MenuItemCell";
id dic=self.tmpMenu.listItems[indexPath.row];
if([dic isKindOfClass:[MenuItem class]]){
MenuItemCell *cell = [self.menuTV dequeueReusableCellWithIdentifier:MenuItemCellIdentifier];
MenuItem *menuItem=(MenuItem *)dic;
cell.UpdateCell(menuItem, [menuItem hasInstoreImage]);
return cell;
}
return nil;
}
I'm doing my project with storyboards and I'm trying to implement a custom UITableViewCell.
I would like to do the following in my custom cell:
#import "CustomCell.h"
#implementation CustomCell
#synthesize myLabel, myButton;
- (instancetype)initWithCoder:(NSCoder *)decoder
{
if ((self = [super initWithCoder:decoder]))
{
//want to custom setup of properties placed in the cell
self.myButton.backgroundColor = [UIColor redColor];//this does NOT work
//and so forth...
}
return self;
}
But the background color is not set. It only works when I set the background color of the button in the tableViewController's cellForRowAtIndexPath function, like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
MyObject *obj = self.myObjects[indexPath.row];
cell.myButton.backgroundColor = [UIColor redColor];//This works!
cell.myLabel.text = obj.name;
return cell;
}
And I have trying debugging by setting break points and NSLog and the initWithCoder gets called before cellForRowAtIndexPath for every cell??
But the background color of the button in the cell does not show when I set it in the custom cell.
Can any help?
Try using the awakeFromNib method instead of initWithCoder: to do any initial customization of the cell. And as mentioned in comments, for simple things like background colors of controls, you can probably just do that in Xcode via the storyboard.
I have created .h and .m files for UITableView called mainTableViewgm.h and mainTableViewgm.m resp. and I am calling -initWithFrame: method from my main view controller to this mainTableViewgm.m implementation file
[[mainTableViewgm alloc]initWithFrame:tableViewOne.frame]
Note that this tableview is in my main view controller. But I have created separate files for the tableView and have also set the custom class to mainTableViewgm in storyboard.
the -initWithFrame: methods appears as follows
- (id)initWithFrame:(CGRect)frame
{
//NSLog(#"kource data");
self = [super initWithFrame:frame];
if (self)
{
[self setDelegate:self];
[self setDataSource:self];
[self tableView:self cellForRowAtIndexPath:0];
[self tableView:self numberOfRowsInSection:1];
// Initialization code
}
return self;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"kource data");
return 1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"kource data2");
UITableViewCell*cellOne =[[UITableViewCell alloc]init];
cellOne.detailTextLabel.text=#"text did appear";
return cellOne;
}
the -initWithFrame: is being called fine along with the 'if (self)' block in this method. But the problem is numberOfRowsInSection: and cellForRowAtIndexPath: are not being automatically called here . kource data/kource data2 never appear in log. What do I do to load the table? Are the delegate/datasource being set incorrectly?
I must mention that I have also set the UITableViewDelegate and UITableviewDataSource protocols:
#interface mainTableViewgm : UITableView <UITableViewDelegate,UITableViewDataSource>
#end
Help will be much appreciated. Thank you.
Your tableview is not loaded when the controller is initializing, so you cannot do that in the init methods. You have to move your code to the viewDidLoad method.
Also you are not setting the delegate and datasource on the tableview object (probably a type, you are setting them on the view controller). It should look like this:
- (void)viewDidLoad:(BOOL)animated {
[super viewDidLoad:animated];
[self.tableView setDelegate:self];
[self.tableView setDataSource:self]; // <- This will trigger the tableview to (re)load it's data
}
Next thing is to implement the UITableViewDataSource methods correctly. UITableViewCell *cellOne =[[UITableViewCell alloc] init]; is not returning a valid cell object. You should use at least initWithStyle:. And take a look how to use dequeueReusableCellWithIdentifier:forIndexPath:. A typical implementation would look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
// Reuse/create cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Update cell contents
cell.textLabel.text = #"Your text here";
cell.detailTextLabel.text=#"text did appear";
return cell;
}
I can't believe I've been doing XCode programming for two years, and still hit this issue.
I had the same problem with XCode 6.1 - I was setting my UITableView's delegate & dataSource in the viewWillAppear function, but none of the delegate functions were kicking in.
However, if I right-clicked on the UITableView on the Storyboard, the circles for delegate and dataSource were empty.
The solution, then, is to hold down the CTRL key, and drag from each of these circles up to the name of your UIView which contains your UITableView:
After doing this, my UITableView happily populated itself.
(So, we're upto v6.1 of XCode now are we ? Do you think Apple ever going to make this thing, you know, friendly...? I would quite like to add a Bookmark in my code... that'd be a nice feature.)
I have some issues with a custom UITableViewCell and how to manage things using storyboards. When I put the styling code in initWithCoder: it doesn't work but if I put it in tableView: cellForRowAtIndexPath: it works. In storyboard I have a prototype cell with its class attribute set to my UITableViewCell custom class. Now the code in initWithCoder: does get called.
SimoTableViewCell.m
#implementation SimoTableViewCell
#synthesize mainLabel, subLabel;
-(id) initWithCoder:(NSCoder *)aDecoder {
if ( !(self = [super initWithCoder:aDecoder]) ) return nil;
[self styleCellBackground];
//style the labels
[self.mainLabel styleMainLabel];
[self.subLabel styleSubLabel];
return self;
}
#end
TableViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NearbyLandmarksCell";
SimoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//sets the text of the labels
id<SimoListItem> item = (id<SimoListItem>) [self.places objectAtIndex:[indexPath row]];
cell.mainLabel.text = [item mainString];
cell.subLabel.text = [item subString];
//move the labels so that they are centered horizontally
float mainXPos = (CGRectGetWidth(cell.contentView.frame)/2 - CGRectGetWidth(cell.mainLabel.frame)/2);
float subXPos = (CGRectGetWidth(cell.contentView.frame)/2 - CGRectGetWidth(cell.subLabel.frame)/2);
CGRect mainFrame = cell.mainLabel.frame;
mainFrame.origin.x = mainXPos;
cell.mainLabel.frame = mainFrame;
CGRect subFrame = cell.subLabel.frame;
subFrame.origin.x = subXPos;
cell.subLabel.frame = subFrame;
return cell;
}
I have debugged the code and found that the dequeue... is called first, then it goes into the initWithCoder: and then back to the view controller code. What is strange is that the address of the cell in memory changes between return self; and when it goes back to the controller. And if I move the styling code back to the view controller after dequeue... everything works fine. It's just I don't want to do unnecessary styling when reusing cells.
Cheers
After initWithCoder: is called on the cell, the cell is created and has its properties set. But, the relationships in the XIB (the IBOutlets) on the cell are not yet complete. So when you try to use mainLabel, it's a nil reference.
Move your styling code to the awakeFromNib method instead. This method is called after the cell is both created and fully configured after unpacking the XIB.