I created a custom UITableViewCell , but want to use the standard UIImageView that is in the cell.
I notice that the frame is always zeroed , which is causing me problems
#implementation MyCustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
// the style is "default" style
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
// I need to override the cells image
if (self) {
self.imageView.image = [UIImage imageNamed:#"MyImageToUseInThisCell"]; // The image view frame is {0,0,0,0}
}
return self;
}
You won't get any frame sizes in the -initWithStyle:reuseIdentifier: because the cell hasn't loaded yet which means the subviews haven't been created yet (i.e. are nil) and hence a 0 frame size.
Even for a viewController, you won't get frame size in the -initWithNibName: method.
You would be better off with:
Frame specific related stuff in -layoutSubviews
Setting an initial property in -awakeFromNib (or directly via IB)
Anyways, try this:
//does this method even run? it shouldn't for a custom UITableViewCell
//unless you've mixed up things unnecessarily
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
NSLog(#"initWithStyle -- %#",self.myImageView);
}
return self;
}
//do frame related stuff here
-(void)layoutSubviews
{
//fyi: this method is called anytime any frame changes
NSLog(#"layoutSubviews -- %#",self.myImageView);
}
- (void)awakeFromNib
{
//fyi: this method is called when the view is pulled from the IB
// Initialization code
NSLog(#"awakeFromNib -- %#",self.myImageView);
//do you thang here
[self.myImageView setImage:[UIImage imageNamed:#"MyImageToUseInThisCell"]];
}
-layoutSubviews on SO
-awakeFromNib on SO
Seems that the problem is when dequeuing a custom cell with a reuse identifier and index path the custom cell base class doesn't set those properties. This is not mentioned in the docs which is very unfortunate.
Related
I am Using StoryBoard and Using Autolayout , I have set Constraints at runtime for Custom cell.Also I have set constraints in ViewDidLayoutSubviews to Handle Device Orientation. So This is taking time for Cell to configure and my cell is not Scrolling Smoothly .Can anyone help me on this?If I have to not set constraints at runtime then where should I set them?Hope I am Clear.Thanks in advance
I'm with nburk, it is not possible to solve with this short detail. But as You are using custom cell in tableView. In the cellForRowAtIndexPath method every time the cell is created so you can use
dispatch_async(dispatch_get_main_queue(), ^{......your code here for custom cell..... }); //used for updating UI
I'm not sure but as your lines it showing the screen UI is not updating on time.
I would suggest you to define a UITableViewCell's subclass and create all constraints in the init/initWithCoder: method.
And don't forget to reuse your cells correctly. In this way you won't re-create your cell's constraints all the time.
EDIT: take a look at the example below.
static NSString* const kCellIdentifier = #"CustomTableViewCellIdentifier";
#implementation CustomTableViewController
- (void)viewDidLoad
{
[self.tableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:kCellIdentifier];
}
- (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
CustomTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
// configure your cell here
return cell;
}
#end
#interface CustomTableViewCell: UITableViewCell
#end
#implementation CustomTableViewCell
- (instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
{
// add subviews and install constraints here. Don't forget to use contentView instead of cell when you install constraints.
}
return self;
}
#end
Earlier I was creating custom cells without prototype, just custom class, register that class for reuse identifier with my UITableView and use like that.
In that case, I used: - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier and that worked great (except for UITableViewHeaderFooterView, they never knew their frame even though that method was called, frame was 0 0 0 0 - but that's not my question here).
Now I was creating some custom UITableViewCells using Interface Builder. Created their custom class, connected outlets from Interface Builder to my custom class and I'v noticed that - (id)initWithCoder:(NSCoder *)aDecoder is being called and that's where I need to do my modifications.
BUT, I wasn't able. That's the only init method being called, but if I try to change font of my label there, it wont be changed. I also tried to round rect button but that is also not working (my button is also having frame 0 0 0 0 - not yet existing) in that method.
My question is, where should I modify things like font, background color of elements created using Prototype Cells?
(id)initWithCoder:(NSCoder *)aDecoder is the correct initializer.
Try this:
#implementation TESTCell
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.textLabel.font = [UIFont boldSystemFontOfSize:25.0f];
}
return self;
}
#end
Other customizations (of own subviews) goes to:
- (void) awakeFromNib
{
[self.myButton setTitle:#"Hurray" forState:UIControlStateNormal];
}
It works fine. Of course, you have to set the identifier of the cell in your StoryBoard or Interface Builder file to the same identifier you use in your UITableViewController.
I use this piece of code in my UITableViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TESTCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = #"TEST";
return cell;
}
I'm using Xcode 5, and want to be using the recommended best practices from Apple, which includes dynamic prototype cells and using registerClass:forCellReuseIdentifier.
I have created a storyboard and dropped a UITableView with 1 prototype dynamic cell on it. I've set the class of the cell to ItemCell, and set the reuse identifier to ItemCell.
The ItemCell class contains a nameLabel IBOutlet, which I've connected to the label within the prototype cell by dragging.
In the ViewController, I register the ItemCell class to be used for the ItemCell reuse identifier:
- (void)viewDidLoad {
[super viewDidLoad];
[_tableView registerClass:[ItemCell class] forCellReuseIdentifier:#"ItemCell"];
}
In tableView:cellForRowAtIndexPath, I dequeue the cell and set the properties for the nameLabel. self.items is an NSArray of strings.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ItemCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ItemCell" forIndexPath:indexPath];
cell.nameLabel.text = [self.items objectAtIndex:indexPath.row];
return cell;
}
So: it's being created as an ItemCell, but it's not loading it from the storyboard. I've confirmed this by overriding initWithStyle:reuseIdentifier and initWithCoder, to see which was being called:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSLog(#"NOT STORYBOARD");
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// Initialization code
NSLog(#"Yay, it's working!");
}
return self;
}
Every time, it's initWithStyle that's being called. From everything I've read, this should be working. I can't find anything that indicates I need to somehow register the class differently when it's in a storyboard, but clearly the cell isn't aware that it's got a storyboard associated with it.
I'm sure I'm making a total newbie mistake, but I can't figure it out. Any help would be appreciated.
You don't have to (and should not) call registerClass for prototype cells defined in the storyboard. initWithCoder is called automatically if a prototype cell has to
be instantiated from the storyboard.
I have a custom cell with a textfield as a subview. Is it legal to set the cell as the delegate for the textfield?
I want to set the cell as the delegate because it's the parent view, but when I do this, the app crashes. I suppose it's because table view dequeue cells and maybe that's why the delegate reference got lost in between.
So, I tried to set the delegate in cellForRowAtIndexPath to make sure it's freshly set every time, but it still won't work.
Do I really have to move a level up and let the tableview controller be the delegate of cell's subviews? or am I missing something else?
crash report outputs:
>libobjc.A.dylib`objc_msgSend:
>
0x32ed4f78: ldr r3, [r4, #8]
>
initWithStyle I have
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier type: (NSString *)cellType
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"Custom Cell" owner:self options:nil];
self = [nibArray lastObject];
self.cellType = [NSString stringWithString:cellType];
self.contentText.autoresizingMask = UIViewAutoresizingNone;
self.detailLabel.autoresizingMask = UIViewAutoresizingNone;
self.contentText.delegate = self;
}
return self;
}
Having the custom cell as delegate to the text field in the cell is not a problem at all. The table view will reuse some cells as and when required but that will not affect anything. I guess you are missing something else. Maybe I could help if you post the error you get when it crashes and corresponding pieces of your code.
Don't set the delegate in cellForRowAtIndexPath.
In your UITableViewCell subclass add the UITextField in the initWithStyle method and then set the delegate to self in there.
I have a UIViewController with an embedded tableview.
The prototype cell has been customized (and subclassed) to add a "slider" effect started by a pan gesture recognizer.
I've followed this tutorial to do it: https://github.com/spilliams/sparrowlike
Inside the cell I've got two views, a front view that slides away, and a back view that stays there showing other controls.
Now I need to add an image to every cell, this image will be chosen between two images depending on a BOOL variable.
The problem is: if I add the image view programmatically, the image will be added to the cell and then when the user swipes away the front view, the image stays there.
So the image should be added to the front view, but I can't do it in the StoryBoard and add an outlet to it.
So the question is: should I put the code to retrieve the image in the custom cell subclass?
If so, where should I put it?
My "CustomCell" class implementation file looks like this at the moment:
#import "CustomCell.h"
#implementation CustomCell
#synthesize frontView;
#synthesize backView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
What about something like this?
- (void)setupCell {
if (yourBoolValueHere) {
[[self frontView] setImage:[UIImage imageNamed:someImageName1]];
} else {
[[self frontView] setImage:[UIImage imageNamed:someImageName2]];
}
[[self view] bringSubviewToFront:[self frontView]];
}
Then, you can just call [cell setupCell] in tableView:cellForRowAtIndexPath:.