I have class hierachy with
ParentCell extends UITableViewCell
ChildCell extends ParentCell
ParentCell have separate XIB, In child cell i was creating and adding only one button to one view at ParentCell XIB. but i cant add action for this button. Because even i was creating a instance for ChildCell, that returns the instance of ParentCell
Because i use loadNibNamed to get the XIB with IBOutlet connections.
# initWithStyle method in ParentCell Class
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self = [[NSBundle mainBundle] loadNibNamed:#"ParentCell" owner:self options:nil]
[0];
}
return self;
}
# initWithStyle method in ChildCell Class
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.button=[[UIButton alloc] init];
[self.contentView addSubview:button];
}
return self;
}
# View Controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ChildCell ";
ChildCell *cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell=[[ChildCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
NSLog(#"Cell : %#", cell); //this have instance of ParentCell instead of ChildCell
}
}
Now temporarily solved by this way
# initWithStyle method in ParentCell Class
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
NSBundle *mainBundle = [NSBundle mainBundle];
NSArray *views = [mainBundle loadNibNamed:#"ParentCell"
owner:self
options:nil];
//Here we are linking the view with appropriate IBOutlet by their tag
self.lblTitle=[views[0] viewWithTag:100];
self.lblContent=[views[0] viewWithTag:200];
}
return self;
}
# initWithStyle method in ChildCell Class
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.button=[[UIButton alloc] init];
[self.contentView addSubview:button];
}
return self;
}
But i don't know this is right approach to follow, or we have some other better approach then this..
You should use the registerNib:forCellReuseIdentifier: method at the viewDidLoad of you UITableView class.
static NSString *parentCellIdentifier = #"parentCellIdentifier";
static NSString *childCellIdentifier = #"childCellIdentifier";
[self.tableView registerNib:[UINib nibWithNibName:#"ParentCell" bundle:nil] forCellReuseIdentifier:parentCellIdentifier];
[self.tableView registerNib:[UINib nibWithNibName:#"ChildCell" bundle:nil] forCellReuseIdentifier:childCellIdentifier];
(Don't forget to set the appropriate ReuseIdentifier at the XIB file)
This is the best practice, and you can get rid of your initWithStyle implementations.
Related
I have custom cell prototype in my storyboard called YesNoTableViewCell. In that cell, I have UITextField.
I want to have the delegate methods of UITextField in my custom cell class.
Here is my code:
MyController
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"YesNoTableViewCell";
YesNoTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
cell = [[YesNoTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
YesNoTableViewCell.m
#interface YesNoTableViewCell : UITableViewCell <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *yesNoInput;
#end
YesNoTableViewCell.h
#import "YesNoTableViewCell.h"
#implementation YesNoTableViewCell
#synthesize yesNoInput = _yesNoInput;
- (id)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_yesNoInput.delegate = self;
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
}
But realized this never called:
(id)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString *)reuseIdentifier
because of:
YesNoTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
never returns nil.
Is there any suggestion for this?
it never return cell nil as you implemented
if (cell == nil){
cell = [[YesNoTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
so just remove this code
I'm having a bit confusion with the UITableViewCellStyle. I want to create a custom UITableViewCell like this:
But the text and the image not appear in the cell when I run the app. In Storyboard,I've put the TableView Style to 'Custom'.
What am I doing wrong?
MainTableViewController
#import "MainTableViewController.h"
#import "CustomMainCell.h"
static NSString *CellIdentifier = #"MainCell";
#interface MainTableViewController ()
//
#property(nonatomic, strong) NSArray *dataSource;
//
#property(nonatomic, strong) NSArray *iconsSource;
#end
#implementation MainTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Currículum Vitae";
// Inicializo los arrays con los datos
self.dataSource = #[#"PERFIL",#"EXPERIENCIA",#"EDUCACIÓN",#"HABILIDADES",#"INTERESES"];
self.iconsSource = #[#"perfil.png",#"experiencia.png",#"educacion.png",#"habilidades.png",#"intereses"];
// Register Class for Cell Reuse Identifier
[self.tableView registerClass:[CustomMainCell class] forCellReuseIdentifier:CellIdentifier];
// This will remove extra separators from tableview
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
// Eliminio las líneas que separan las celdas
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
- (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 self.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomMainCell *cell = (CustomMainCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[CustomMainCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.title.text = self.dataSource[indexPath.row];
cell.icon.image = self.iconsSource[indexPath.row];
return cell;
}
CustomMainCell.h
#interface CustomMainCell : UITableViewCell
//
#property (weak, nonatomic) IBOutlet UILabel *title;
//
#property (weak, nonatomic) IBOutlet UIImageView *icon;
#end
CustomMainCell.m
#import "CustomMainCell.h"
#implementation CustomMainCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.textLabel.textColor = [UIColor brownColor];
}
return self;
}
- (void)awakeFromNib
{
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
You shouldn't have:
// Register Class for Cell Reuse Identifier
[self.tableView registerClass:[CustomMainCell class] forCellReuseIdentifier:CellIdentifier];
because the cell is registered from the storyboard when the view controller is unarchived. By registering the class you are removing the archive (NIB) registration.
Additionally:
if (cell == nil) {
cell = [[CustomMainCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
shouldn't be required as you will always get a valid cell back (because the identifier is registered)
I am not sure if this is a Storyboard Bug. I have created a project with a custom cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"HomeGameTurnCell";
HomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[HomeTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
The custom cell has some image views. One of the image views is a subclass.
#interface HomeTableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet RoundedProfilePicture *profilePictureImageView;
#property (strong, nonatomic) IBOutlet UIImageView *turnThumbnailImage;
#property (strong, nonatomic) IBOutlet UILabel *usernameLabel;
#property (strong, nonatomic) IBOutlet UILabel *lastPlayedLabel;
#end
The RoundedProfilePicture subclass simply has the following:
-(id)init {
NSLog(#"%s",__PRETTY_FUNCTION__);
self = [super init];
if (self) {
[self setupView];
}
return self;
}
- (void)setupView
{
NSLog(#"%s",__PRETTY_FUNCTION__);
self.clipsToBounds = YES;
self.layer.cornerRadius = self.bounds.size.width / 2;
self.layer.borderWidth = 3;
self.layer.borderColor = [UIColor darkGrayColor].CGColor;
}
What I am finding is that the RoundedProfilePicture methods are not being called. Within the storyboard I have setup one prototype cell and the correct identifier. I also have the image view set as the correct Custom class. But it doesn't seem to take effect, is there something I am missing/could check?
When you are reusing an element you should override UITableViewCell method:
-(void)prepareForReuse
instead of init. It will called each time.
If you want one time initialization you should do it in: initWithStyle:reuseIdentifier:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//do your stuff here
}
return self;
}
From apple docs:
If you registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its initWithStyle:reuseIdentifier: method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead.
The issue is that when a UIimageView is subclassed and called from the storyboard a different method is called for the init.
-(id)initWithCoder:(NSCoder *)aDecoder {
// As the subclassed UIImageView class is called from storyboard the initWithCoder is overwritten instead of init
NSLog(#"%s",__PRETTY_FUNCTION__);
self = [super initWithCoder:aDecoder];
if (self) {
[self setupView];
}
return self;
}
This resolves the issue of the rounded profile view not being called.
A weird thing just happend, i trying to build my custom tablecell but my initWithStyle is not called.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
My Tablecell looks normal:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSLog(#"xx1111");
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
How i'm trying to load the Customcell nib:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *TFDCustomCell = #"TFDCell";
TFDCell *cell = (TFDCell *)[tableView dequeueReusableCellWithIdentifier:TFDCustomCell];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TFDCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[TFDCell class]])
cell = (TFDCell *)oneObject;
}
return cell;
}
But the NSLog(#"xx1111"); doenst appear in my logs. When i place a NSLog in 'setSelected' it works 'fine'
The solution was simple
-(id)initWithCoder:(NSCoder *)aDecoder
{
NSLog(#"initWithCoder");
self = [super initWithCoder: aDecoder];
if (self)
{
}
return self;
}
As I know if you load your view (in current case cell) from nib initWithStyle: method wont be called. Overload awakeFromNib: method instead to make custom initialization.
You're not initializing the table's cells with initWithStyle so initWithStyle of your custom cell won't be fired. But awakeFromNib will be called for your initialization call of dequeueReusableCellWithIdentifier.
Let's say i have created myTableViewCell class:
#import "MyTableViewCell.h"
#implementation MyTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
UIImageView *imgSponsor=[[UIImageView alloc ] initWithFrame:CGRectMake(0,0,self.bounds.size.width,200)];
imgSponsor.image = [UIImage imageNamed:#"imageFirstCell"];
[self.contentView addSubview:imgSponsor];
}
return self;
}
[...]
#end
Can I set a default height for this cell from within this class apart from running through the associated tableviewcontroller method (heightForRowAtIndexPath..)?
You can use like in MyTableViewCell.h-
-(void)layoutSubviews {
self.frame = //Define your frame;
[super layoutSubviews];
}