Creating a custom UITableViewCell - objective-c

I'm trying to create a custom UITableViewCell.
From XCode 4.6 interface builder, I've set the Style property of the cell to Custom. And added controls to the cell using drag and drop. 2 UILables and a UIButton. It looks like this.
I created a separate class deriving from UITableViewCell to assign the properties of the 3 UI elements and make the changes there. I've set the cell's custom class as DashboardCell from the Identity Inspector as well.
DashboardCell.h
#import <UIKit/UIKit.h>
#interface DashboardCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *numberOfMails;
#property (weak, nonatomic) IBOutlet UILabel *mailType;
#property (weak, nonatomic) IBOutlet UIButton *numberOfOverdueMails;
#end
DashboardCell.m
#import "DashboardCell.h"
#implementation DashboardCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.numberOfOverdueMails setBackgroundColor:[UIColor colorWithRed:244/255.0f green:119/255.0f blue:125/255.0f alpha:1.0f]];
[self.numberOfOverdueMails setTitle:#"lol" forState:UIControlStateNormal];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
In the TableViewController, I have modified the following method to return my custom cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
DashboardCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[DashboardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
My problem is even though the custom button shows up, the changes I've done (changing the background color of the button, changing the title of one UILabel) aren't showing up. What seems to be the mistake here?

The method initWithStyle:reuseIdentifier: will not be called because you're using interface builder to create a cell.
You can set the background color and title by overriding the method awakeFromNib.
You can also set these in the method tableView:cellForRowAtIndexPath:

If you get your cell from a xib or storyboard, dequeueReusableCellWithIdentifier:forIndexPath: will always return a cell -- if one exists it will reuse it, if not it will create one from the template in IB. Therefore, your if(cell ==nil) clause will never be satisfied, and in fact is no longer needed. If you want to use an init method, then use initWithCoder:

Related

An IBOutlet is not initiated

I'm trying to make a tableview with customized cells.
I've created 2 files .m and .h that refer to my class CustomCell.
Here is the code :
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
{
IBOutlet UIImageView *miniLogo;
IBOutlet UILabel *cellText;
IBOutlet UIButton *deleteButton;
}
#property (strong, nonatomic) IBOutlet UIImageView *miniLogo;
#property (strong, nonatomic) IBOutlet UILabel *cellText;
#property (strong, nonatomic) IBOutlet UIButton *deleteButton;
#end
-------------------------------------------------------------
#import "CustomCell.h"
#implementation CustomCell
#synthesize miniLogo,cellText, deleteButton;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
}
/*
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}*/
#end
With a .xib file, i've designed my cell, and connected the IBOutlets and set the Identifier for the cell.
In the table with my customized cells, I call the cells in the method tableView:cellForRowAtOndexPath: like this :
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CellsIdentifier ];
if (cell == nil){
UIViewController *tempVC = [[UIViewController alloc] initWithNibName:#"CustomCell" bundle:nil];
cell = (CustomCell *)tempVC.view;
}
When I launch my app, the labels display the texts set, and the image views show the right images. But the buttons don't appear. In fact, when setting break points, I seen that address for my buttons are always 0x00000000 (means that my buttons aren't initiated).
Can someone help me to solve this problem please.
I've found the problem.
In he viewDidLoad I was doing :
UINib *cellNib = [UINib nibWithNibName:#"myCells" bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:CellsIdentifier];
But because I have changed my .xib name, I wasn't loading the right interface.
What is strange is that I don't have any nib named "myCells".
Maybe I should have perform a "clean" on my project...

UITextField inside UITableViewCell won't become first responder

I use a UITableViewController to display the details of KoreanFood. The first cell is a custom UITableViewCell (OverviewCell) with an Image and two UITextFields, which I created and layout in Storyboard (AutoLayout).
I subclassed UITableviewCell like this:
// OverviewCell.h
#interface OverviewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UITextField *englishTitleTF;
#property (strong, nonatomic) IBOutlet UITextField *koreanTitleTF;
#property (strong, nonatomic) IBOutlet UIImageView *myImageView;
#property (strong, nonatomic) UIImage *thumbnail;
My textfields in Storyboard are set to enabled/UserInteractionenabled and the delegate is my TVC. When I create the cells I also do this in code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == GENERAL_SECTION) {
static NSString *overviewCellID = #"overviewCell";
OverviewCell *overviewCell = [tableView dequeueReusableCellWithIdentifier:overviewCellID forIndexPath:indexPath];
overviewCell.englishTitleTF.text = self.generalInfo.titleEnglish;
overviewCell.koreanTitleTF.text = self.generalInfo.titleKorean;
overviewCell.englishTitleTF.enabled = YES;
overviewCell.koreanTitleTF.enabled = NO;
//BOOL test = [overviewCell.englishTitleTF becomeFirstResponder];
overviewCell.koreanTitleTF.delegate = self;
overviewCell.englishTitleTF.delegate = self;
UIImageView *imageView = [[UIImageView alloc] initWithImage:self.generalInfo.thumbnail];
overviewCell.myImageView = imageView;
overviewCell.myImageView.frame = CGRectMake(25, 25, 95, 95);
[overviewCell addSubview:overviewCell.myImageView];
return overviewCell;
}
The comment with the BOOL is NO, and I just don't know why... As I set the text and it's displayed correctly, I know the Outlets are set and the Cell isn't nil (I checked that).
Why does this not become first responder?
I also tried some suggestions inside the OverviewCell subclass like the hit test or implementing the setSelected: / setEditing: methods. But a becomeFirstResponder to the textField here doesn't change anything as well.
A view can't become first responder until it's been added to the responder chain, which happens automatically when the view gets added as a subview of a view that's in a window in the application object's windows list. In other words, your cell hasn't been added to the table view yet, so it's not connected to anything, and hence can't become first responder.
Try sending becomeFirstResponder from a method that gets called after the table view has finished loading its cells. And of course, don't do this:
overviewCell.koreanTitleTF.enabled = NO;

Is it possible to create a static tableview in a storyboard and add it programmatically later? (iOS5, XCode 4)

I'm trying to create a couple of small, static tableviews and add them to a panel that I have which slides in and out. The panel is created programmatically so I can't lay the tableviews out inside it via storyboard, and anyway I'm not sure if this is possible anyhow: It seems the only way you can lay out static tableviews that work is in a tableviewcontroller, which takes up the whole screen.
If you can't tell I'm pretty new to iOS dev so if I'm not understanding some fundamental concepts here please feel free to explain.
Of course is possible. Here is how it can be done:
Drag a TableViewController to your storyboard.
Set its Size to Freeform, add an identifier and uncheck Resize View From NIB
Select the tableview and set its content to Static Cells. Design your cells.
Set its size
Now, wherever you need to instantiate it do it like this:
// I am using a UITableViewController as an example here
// you probably would like to set your actual controller subclass instead
UITableViewController *tableViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"staticTv"];
UITableView *tableView = tableViewController.tableView;
[self.view addSubview:tableView]; // Or add it to whatever view
Enjoy :)
A UITableViewController isn't necessary to provide the functionality you need to manage a UITableView. I think what you're looking for is the "Delegate" pattern. Any UIViewController can be assigned to be the delegate of the UITableView. For example, I have a "static" table that shows some options in an app I am working on:
#interface LBOptionsViewController : UIViewController <UITableViewDataSource,
UITableViewDelegate>
If you're creating your table views programmatically, you'll probably either be creating them in viewDidLoad or loadView (if you're creating the actual view yourself). After you've created your tableView, assign the delegates:
self.tableView.delegate = self;
self.tableView.dataSource = self;
Then your UIViewController subclass will receive the data delegate messages like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Not sure if this helps you. I have not played with Storyboards much yet.
EDIT: #Alladinian has the right answer! If you're using an property for the view controller make sure you allocate it if you need it to be called by other methods.
I've yet to find a usefully reason to use static table view cells over dynamic. Table views were pretty scary when I started iOS programming. I used sqlite in my first app YIKES.
So yeah, you should just import the UITableView Data Source and Delegate and follow up by adding the table view to your panel (assuming it's a uiview and you can add the table view as a subview).
Anyways in your ViewController.h include UITableViewDataSource and UITableViewDelegate.
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
Next, add properties for a UITableView and an NSMutableArray:
#property (strong, nonatomic) UITableView* tableView;
#property (strong, nonatomic) NSMutableArray* tableViewContents;
In your ViewController's .m:
#synthesize tableView;
#synthesize tableViewContents;
inside ViewDidLoad:
self.tableViewContents = [NSMutableArray arrayWithObjects:#"Cell 1",#"Cell 2",#"Cell 3",nil];
[self.tableView setDelegate:self]
[self.tableView setDatasource:self]
In the .m file:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.tableViewContents.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSUInteger row = [indexPath row];
index = row;
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
cell.textLabel.text = [tableViewContents objectAtIndex:row];
return cell;
}

How to push a view from a UITableViewCell

I have a UITableViewController that has custom cells that I have customized with my own subclass.
In this subclass, I've added a button and I want to push a view into the stack of the navigation controller. I have no idea on how to do that since I don't know how I can access to the navigation controller from my custom cell.
Any idea?
Need more infos here. What class holds the table, what class is the tableview delegate?
In the most simple case you're working in one single class. Than it would be [self.navigationController pushViewController: xyz].
But if you have your own subclassed UITableViewCells, than you need to communicate between the cell class and the viewcontroller. You could do this via setting a property in the cell class or your own customCell delegate.
You could also just send a Notification ([[NSNotificationCenter defaultCenter] postNotification: #"cellButtonTouchedNotification"]) on which your viewController is listening ([[NSNotificationCenter defaultCenter] addListener: self target: #selector(...) name: #"cellButtonTouchedNotification"]). In this case you could use the userInfo property to remember which cell was touched.
Anotherway is to make the button accessible from outside (a property eg). Then you could add the target in your tableViewDelegate's method cellForRowAtIndexPath:. Smth. like [myCustomCell.button addTarget: self selector: #selector(...)]; You could use tags to identify the row myCustomCell.button.tag = indexPath.row.
Use delegation. Here a simple example.
//.h
#protocol MyTableViewCellDelegate;
#interface MyTableViewCell : UITableViewCell
#property (assign, nonatomic) id <MyTableViewCellDelegate> delegate;
//your code here
#end
#protocol MyTableViewCellDelegate <NSObject>
#optional
- (void)delegateForCell:(MyTableViewCell *)cell;
#end
//.m
#implementation MyTableViewCell
#synthesize delegate = _delegate;
- (void)prepareForReuse {
[super prepareForReuse];
self.delegate = nil;
}
- (void)buttonAction {
if ([self.delegate respondsToSelector:#selector(delegateForCell:)])
[self.delegate delegateForCell:self];
}
#end
When you click the button you send a message to the delegate for your cell (e.g. the table view controller that is inserted into the navigation controller).
Here the controller
#implementation YourController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *reuseIdentifier = #"MyCustomCell";
MyTableViewCell *cell = (id)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil)
cell = [[[MyTableViewCell alloc] initWithMyArgument:someArgument reuseIdentifier:reuseIdentifier] autorelease];
[cell setDelegate:self];
// update your cell
return cell;
}
- (void)delegateForCell:(MyTableViewCell *)cell {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// do your stuff
[self.navigationController pushViewController:...];
}
#end
Hold a pointer to your UITableViewController in the cell. You can pass it in the cell's constructor or set it later. Then you can call the pushViewController on the table view controller.
Even more beautiful solution would be to define a delegate for your cell, say ButtonCellDelegate that has a buttonClicked callback. You implement the delegate in your UITableViewController (or any other place where you have access to the view controller). Then you pass the delegate to the cell as described above and call the callback function from the cell when the button is clicked.

Editable UITableView with a textfield on each cell

I am new to the iOS world and I want to know how to make a UITableView with custom cells that look and behave like the one you have when you try to configure some WiFi connexion on your device. (You know the UITableView with cells containing UITextFields with blue font where you set up the ip address and all that stuff... ).
To make a custom cell layout do involve a bit of coding, so I hope that dosen't frighten you.
First thing is creating a new UITableViewCell subclass. Let's call it InLineEditTableViewCell. Your interface InLineEditTableViewCell.h could look something like this:
#import <UIKit/UIKit.h>
#interface InLineEditTableViewCell : UITableViewCell
#property (nonatomic, retain) UILabel *titleLabel;
#property (nonatomic, retain) UITextField *propertyTextField;
#end
And your InLineEditTableViewCell.m could look like this:
#import "InLineEditTableViewCell.h"
#implementation InLineEditTableViewCell
#synthesize titleLabel=_titleLabel;
#synthesize propertyTextField=_propertyTextField;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Here you layout your self.titleLabel and self.propertyTextField as you want them, like they are in the WiFi settings.
}
return self;
}
- (void)dealloc
{
[_titleLabel release], _titleLabel = nil;
[_propertyTextField release], _propertyTextField = nil;
[super dealloc];
}
#end
Next thing is you set-up your UITableView as you normally would in your view controller. When doing this you have to implement the UITablesViewDataSource protocol method - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. Before inserting your implementation for this, remember to #import "InLineEditTableViewCell" in your view controller. After doing this the implementation is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
InLineEditTableViewCell *cell = (InLineEditTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"your-static-cell-identifier"];
if (!cell) {
cell = [[[InLineEditTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"your-static-cell-identifier"] autorelease];
}
// Setup your custom cell as your wish
cell.titleLabel.text = #"Your title text";
}
That's it! You now have custom cells in your UITableView.
Good luck!