I have created a custom UITableViewCell using storyboards.
I am loading it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
HJAddCell *cell = [tableView dequeueReusableCellWithIdentifier:#"addCell"];
// setting properties of cell
return cell;
}
Now I need to add some more customization programmatically during initialization. But the problem is that in my HJAddCell, the init methods are not called. What should I do so that the the init method is called or is there any other way to add more customization code to the custom cell class at init time with minimum effort.
Update: I need to add borders to UILabels. I didn't find any thing to do this in storyboard.
UIViews have two designated initializers: initWithFrame: and initWithCoder:. initWithCoder: is used if view is loaded from nib/storyboard, initWithFrame - if view is created programmatically.
Of course the init methods are not being called. You are not calling any init methods yourself. If you are registering HJAddCell's nib with the table view, the initialization method being called is initWithCoder. If you are using a nib, you should also ask yourself about why do you need to customize a custom UITableViewCell on the runtime. I mean, it's already a custom cell. You should create the cell with the properties you need to have.
How about adding a new method to HJAddCell called reset? Move the initialization code that needs to happen before each use into the reset method and call reset from your initializer.
Then you would call the reset method after dequeue-ing your cell.
Related
I'm using Xcode Version 5.0 (5A1413) and targeting iOS7 for iPhone. I have nothing but a UIViewController with a Table View on it. Everything I've found says to just set the table's dataSource and delegate to the ViewController. No matter what I do the app just crashes immediately. The view never loads even though there's no code written manually at all. Is it no longer possible to put a table onto a non TableViewController?
You should implement all required methods from UITableViewDataSource for preventing crash:
#required
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
I think you did a common mistake from beginners. You deleted the main view in Interface Builder and then added a Table View.
You have to know that the UIViewController class has a UIView property called view. By default this property is set to the view created with the nib (or Storyboard) file.
To say that the Table View is the main viewcontroller's view you have to right click from the file owner to the tableview and select "view".
Another method if your not comfortable yet with this method is to set the tableview in the first view.
Here is the answer - How to add static TableView to ViewController
Really you need to implement required methods.
There is no such restrictions with iOS 7.0. You can still have a UIViewController in-act as a data source and delegate for UITableView. You just need to implement all the mandatory methods set by these protocols. Could you please update your question with your code and the exception causing the crash. Without this information it would be hard to provide a solution.
I am especially interested in seeing your cellForRowAtIndexPath the way you are constructing cells and reusing them.
Add the Delegate and Datasource to the interface:
#interface MyViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate>
I want to add a tableview-look-a-like-login to my app, but it seems to be not that easy to implement. I tried to accomplish my goal using more then one approach, but i am not sure about which solution is the best.
For example, Dropbox and Facebook have a login page like this.
Here are my 3 approaches :
I added 2 UITextfields to my View (no border) and placed a . png behind, which looks like a tableviewcell. ( Not the best approach cause i want to use real tableviews )
I added a Container View to my ViewController placed a tableview with static Table Views inside. The Problem here is, that i dont know how to access the information inside my viewcontroller?
I added a tableview to my ViewController and used dynamic cells with it. Connected the outlets for delegate and datasource to my viewcontroller and initialized them with the delegate and datasource methods. The Problem here is, that i can not use static table views inside a uiviewcontroller.
Is there any better way of solving this problem ?
I would really like to know how to do this in a more elegant way.
EDIT:
A ContainerViewController basically solved this issue for me some month ago.
After embedding one into the main controller you can access it through the prepareForSegue function and define a protocol-based interface for that specific controller to interact with the embedded controller.
If you want to use static cells inside a regular UIViewController, just add the static cells and design them the way you like in interface builder, then connect the table cells as strong IB outlets (weak won't work, make sure they are strongly referenced). This will work flawlessly if you have a few table cells. Then set the view controller as the data source of the tablet view, implement -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section to return the number of cells and implement -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to return your strongly referenced cell instance for the specified index path. I've used this method for a simple table view in my view controller that had four cells and it is working perfectly. For a large-dynamic data set, I definitely do not recommend this approach but for small, static tables, this does the job right.
I have an idea how to solve this. I think it's a clean way to do so. You do not need storyboard for this controller.
Make your controller subclass UITableViewController like so:
#interface YourViewController : UITableViewController
Then in your viewDidLoad you create the instances of the cells:
- (void) viewDidLoad {
usernameCell = [YourTextFieldCell new];
passwordCell = [YourTextFieldCell new];
}
The YourTextFieldCell is of course your own subclass of a UITableViewCell, which could be something like this:
#implementation YourTextFieldCell {
UITextField textField;
}
- (id) init {
self = [super init];
if (self) {
// Adjust the text's frame field to your liking
textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
[self addSubview:textField];
}
}
// A getter method to access the textfield from the outside
- (UITextField *) textField {
return textField;
}
#end
Back in YourViewController:
- (NSInteger) tableView:(UITableView *) tv numberOfRowsInSection:(NSInteger) section {
return 2;
}
- (UITableViewCell *) tableView:(UITableView *) tv cellForRowAtIndexPath:(NSIndexPath *) indexPath {
if (indexPath.row == 0) {
return usernameCell;
} else if (indexPath.row == 1) {
return passwordCell;
}
return nil;
}
Do you get where I am going with this? This is how I think you should do it! Good luck!
I think your approach 2 is the best. If you need to access information in the table view controller, from your UIViewController (which will be the parent view controller), you can get a reference to that table view controller with self.childViewControllers.lastObject. In the viewDidLoad method of the UIViewController subclass, you could set yourself as the delegate of the table view with this line if you want:
[[(UITableViewController *)self.childViewControllers.lastObject tableView] setDelegate:self];
That way, you could implement the tableView:didSelectRowAtIndexPath: method in the view controller, which will get the information I'm guessing you need.
If you go with your option 2) using a storyboard and have a ContainerView containing your own subclass of UITableViewController with static cells then you can implement the prepareForSegue: method in your parent ViewController to take a reference to the UITableViewController (it'll be the destinationController of the segue) and also to pass itself down to the UITableViewController subclass if necessary (which should hold onto it with a weak reference).
Disclaimer - This answer will work for any size of UITableView, but if you're just making a login view, Tom's answer will work quite well.
I'm not sure if this will help, but what I did for this was create my own UITableView-esque subclass with a UITableViewCell-esque subclass as well.
This may not be what you want to hear, but I find what I made to be really helpful, since I've used it a number of times now. Basically, you have a UIView with the stylistic approach for the different types (10.0f - 20.0f cornerRadius and a 1px border (divide by UIScreen's scale property for retina). As for the cell, you'll want to have a full sized UIButton on it that responds to your table view for the touch events either with a delegate or by setting the target and tag inside your table view's class.
Last, you'll have a delegate system just like the UITableView for your information for building the specific tables.
In short, you'll need:
2 UIView subclasses (TableView and TableViewCell)
2 Delegates/Protocols (TableViewDataSource and TableViewDelegate)
Optionally
1 Delegate (TableViewCellResponseDelegate)
1 NSObject Subclass (Contains all of the information needed in each cell - Ease of use)
I found Can's solution to be the best / easiest, but unfortunately it breaks in XCode 5.1 --
I found a workaround which builds off the same basic idea, but unfortunately requires a little more involvement: http://www.codebestowed.com/ios-static-tableview-in-uiviewcontroller/
To summarize, you can add TableViewCells directly to views (and create IBOutlets from them, etc), but in order for them to get "moved" to the TableView properly, you need to remove them from the view in code, and you also need to set Auto-Layout constraints in IB.
I have an app that gives you a popOver Tableview controller. This popOver has n number of cells. I need to be able to call a function on the main view whenever one of the table cells in the popover is touched. How would I go about doing that?
It would be nice to able to dismiss the popover too once a cell is touched....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell check %#", [totalArray objectAtIndex:indexPath.row]);
[self dismissPopoverAnimated:YES];
}
Here it will print the console the text in the cell, but I need to be able to pass that cell text back to the mainview... oh yeah, and the [self dismisspopoveranimated: yes] doesn't work...
The traditional way to address this is to make your "main" view controller a delegate of the class with the table view:
Define an application-specific delegate protocol, add a delegate method specific to your use case, and change your "main" view controller to conform to it. The delegate method should take a parameter whose type is the data you want to pass to the "main" view controller (in your case, either a UITableViewCell or the NSString containing its text).
Create a property on the class with the table view whose type is the new delegate protocol.
Pass your "main" view controller into the class with the table view by setting it as the value of the new property.
When the tableView:didSelectRowAtIndexPath: method is called, invoke the delegate method on the delegate you have created, passing in the data you want to give to the "main" view controller. This gives your "main" view controller an opportunity to respond to the fact that the table cell was selected in the popover, and it can call whatever function you need it to.
You can actually do the same thing in less code with blocks, but it's a bit more advanced. I won't get into that here.
You could also just use NSNotificationCenter, but personally I try to avoid using that unless absolutely necessary.
I have a inherited UITableViewCell class from which I create a custom cell containing a UITextField.
The UITextField is 25 pixel height by default.
The behavior I want is that when the user clicks in the textField, the UITextField should change to 100 pixel height and the cell should grow accordingly.
I can detect when the UITextField receive focus thanks to notifications and observers but I wonder how to programmatically make that tableView:HeightForCellAtindexPath: be called.
Like Endemic says, tableView:heightForCellAtIndexPath: is the method you need to implement. According to Apple, the most efficient way to trigger a resize is an empty beginUpdates / endUpdates block, like this.
[tableView beginUpdates];
[tableView endUpdates];
It saves you the overhead of reloading the cell contents and, I believe, gives you a nice animation you wouldn't otherwise get from reloadData.
You must have a link between the cell and the table view controller. Since you are already creating your custom cell in your controller the easiest way would be to use the delegate pattern.
#class CustomTableViewCell;
#protocol CustomTableViewCellDelegate
- (void)customTableViewCellDidEnterTextMode:(CustomTableViewCell *)cell;
#end
#protocol (nonatomic, assign) id<CustomTableViewCellDelegate> delegate;
and just call the delegate method where you are detecting when the text field gets focus
[self.delegate customTableViewCellDidEnterTextMode:self];
and in the controller
- (void)customTableViewCellDidEnterTextMode:(CustomTableViewCell *)cell {
self.editingIndexPath = [self.tableView indexPathForCell:cell];
// from Jablair's answer
[tableView beginUpdates];
[tableView endUpdates];
}
And then in tableView:heightForCellAtIndexPath: just return your special height for self.editingIndexPath.
You would probably have to include another delegate method to know when focus is leaving the text field as well.
Another approach would be to use notifications but that will just complicate your code and if there is only one receiver of the message a delegate is the preferred way. A third approach would be to set the delegate of the text field to your controller instead of to your cell.
The bottom line, you need to provide the link between the cell and table view your self and I believe using a delegate pattern is the best approach.
The tableView:heightForCellAtIndexPath: method is called whenever the table view loads data, so simply calling reloadData (or one of the other, more selective reload methods) on the table view should work fine.
I'm using custom headers for my tableview...
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CustomHeaderController *header = [[CustomHeaderController alloc]
initWithNibName:#"TableHeader" bundle:nil];
header.title.text = #"Test";
return header.view;
}
The title label is never set though. I even tried creating a viewWillAppear method and setting it there, but that didn't work. My outlets are set up too!
Thanks!
SOLUTION: View wasn't load on the return header.view call. Call header.view or add a viewDidLoad method to the class of header to get it to work! Thanks all!
So I'm not sure "title" here means what you think it means. Setting the title on a view controller isn't going to affect what's in the view. If you're trying to set a title on a section, you may just want to use tableView:titleForHeaderInSection. tableView:viewForHeaderInSection is for more complex section headers (like if you wanted to put buttons, or multiple rows of text or something like that). If it matters, you can use both of these methods. I'm not 100% certain on the order they're called, but I'm pretty sure it looks for a viewForHeaderInSection and then if that's nil it goes to titleForHeaderInSection.
The function expects a UIView to be returned, but you appear to be returning a UIViewController. Did you mean to do return header.view?