Drawing selected state on custom UITableViewCell - iphone

I have a customer UITableViewCell whose whole display is drawn indrawRect. When it draws it creates CGLayers so that it can reuse certain pieces when something is changed.
I have changed my UITableViewCellSelectionStyle to "None" because I don't want the default selected view to cover my drawing.
My problem is that I call setNeedsDisplay in setSelected:animated: for my cell but by the time drawRect is called, setSelected:animated: has already been called again to deselect the cell. In my table view controller didSelectRowAtIndexPath, I call deselectRowAtIndexPath as Apple advises.
EDIT - I have also tried called the setNeedsDisplay on my cell from my table view controller's (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath but that didn't change anything.
Any ideas? Thanks.

Use the table cell's selectedBackgroundView property. If you assign a custom view to that, it'll get shown and hidden at the same time as the default selection backgrounds—in other words, without having to wait for the setNeedsDisplay to get around to calling drawRect: on the cell itself.

Related

How to set the height of subviews of UItableViewCell when cell is created programaticalliy

I have custom cell created with nib. In the table view I am using the method -(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath to set the height of cell
Everything works fine.
But the problem is that I also want to change the size of UIlabel which is added as subview in nib of cell.
How do I do that?
Which metod to override in customcell class ?
The method you are looking for is:
-(void)layoutSubviews
{
[super layoutSubview];
//Do your magic
}
layoutSubviews is call after the cell is created and whenever the device orientation changes, to allow you to resize and/or move the subviews (plus make any other minor adjustments) to compensate for differences between the orientations, but in this case you can also use it to redraw your subviews.
As you have UILabel in your custom cell class, make a function in that class that take frame you want to set as parameter. Set frame of label in that function. You need to call this function from your cellForRowAtIndexPath method before returning the cell.
If you are reusing your custom cell you should call method only when the (cell == nil)
Also if you can add some code in your question that would be helpful and you can get more precise answer.
You need to treat your custom cell in the same way you would treat a normal view or view controller class with a xib. i.e. You need to create IBOutlets for the controls in you custom cell and during creation of the cell you can access the controls quite easily.
myCell.myCustomUILabel.text = #"blah"
There are some gotchas when using custom cells in a xib and onnecting up the IBOutlets. This SO answer (of mine) explains how to create and link up IBOutlets of a custom cell.

How to have an editable multine UITextView in UITableViewCell

I've got a UITextView inside a UITableViewCell subclass. I have no problem getting the new height of the Text view and cell. The problem I have is telling the UITableView to update.
I have implemented heightForRowAtIndexPath: to return the live height of the cell as the TextView expands.
But somewhere `[tableView beginUpdates]; [tableView endUpdates]; must be called.
How? Should I add a delegate property to the UITableViewCell which is set to the UITableViewController subclass? And then send a delegate message when the cell expands height and the Tableview needs to update? It seems a little weird to have a delegate between the UITableViewCell and Controller?
I tried using NSNotificationCenter, but I have more than one editable cell, and more than tableview of this nature. So there is no way to register only for notifications for the cells without copying and pasting the same line over again, which isn't nice (as the cells are created in IB, and are not in an array or set), and having multiple tableviews means an exception occurs on the other table view as it is told to update but nothing changes.
I've seen lots of questions and answers on this topic, but when it comes to updating the tableview they all just say "now update the tableview" and not how to. So how do I telly he tableview to update, from one of it's cells?
I would think that this behavior would be best implemented in the UITableViewController instead of the view itself (the UITableViewCell).
Your controller is responsible for setting cell height, and typically will be the delegate for your UITextView's, so let it handle all of this.
In your textViewDidChange method, figure out what the new height of your cell should be, update your data structure to reflect that, and call reloadRowsAtIndexPaths:withRowAnimation: to have it actually change.
Edit:
So since you didn't like my first suggestion, another way to do this would be to add a recommendedRowHeight property to your custom UITableViewCell.
Then, you can either observe this property from your UITableViewController or implement a delegate protocol with a method along the lines of:
- (void)recommendedRowHeightDidChange
// or
- (void)recommendedRowHeightDidChangeTo:(CGFloat)newHeight
Then, when your height changes, update your recommendedRowHeight property and call your delegate's method if you go that route.
Either way, once your controller figures out that the recommended row height of a cell has changed, it can do what it is supposed to do. Update your data structures reflecting the current row heights and then call reloadRowsAtIndexPaths:withRowAnimation:.
You can add your tableview controller object as a weak reference to your tableview cell class. And in tableview controller you can have a method which will be called from tableview cell class.

UITableViewCell Loses Properties After Deselection

I have a custom subclass of UITableViewController that uses a set of custom subclass of UITableViewCells.
There is nothing too fancy. The table cell's have two subviews in their content view.
Right now I have the user select a cell, a navigation controller pushes to a color selection screen. The user selects a color. Great. The table view is a delegate of this view. It receives the color, updates the selected cell so that the subview now uses this background color, and then pops the current view from the navigation controller.
It all works! The tableViewController slides back into view and the indicator in the cell now displays the background color the user selected. BUT after the table view appears, the cell selected cell gets a blue highlight applied to it automatically. This is some built in magic from the TableViewController. It's nice but it's resetting my cell's indicator status.
Problem is, I can't find out where to properly update the cell to this new color. The color is stored as an ivar in the tableViewController. So I thought trying this out at:
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
But alas this method doesn't fire as a result of the magic UIKit is doing to briefly highlight the cell. Any ideas where I need to update my cell properly?
I'm not sure how you are setting the background color yet, but you should be using the backgroundView property of your UITableViewCell subclass.
someCell.backgroundView.backgroundColor = someColor;
(from inside your UITableViewController, or wherever you are getting that color information back from your picker).
Doing this should not reset the background color when the cell goes from selected to deselected.

Decrease UITableViewCell width and put custom button on the left

Is there any way to decrease the standard width of grouped UITableViewCell and put a custom button on the left side(outside of cell boundary)? I tried to change the cell size but it keeps same
You are going to have to fake the editing mode.
What I mean by that is that as AtomRiot said you have to subclass UITableViewCell so that when in editing mode you show the button you want on the left, outside the cell.
But first things first.
To change the indentation level for your cells all you need to do is implement this delegate method for the UITableView
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
So that takes care of it. Then in your UITableViewCell subclass all I would do is to implement the method
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
which I assume is called when the table the cell belongs to has changed to editing mode.
There I would fade in (or animate in any way you want) a button to appear on the left of your cell.
I have done it inside a grouped-style cell but never on the outside.
Give it a try!
You could subclass UITableCell and add your own custom views inside of it. I have not personally added a button inside one but it should work. It may get confused with the row selected call the tableview makes if you are implementing that.
The Cocoanetics blog seems to have a pretty good solution to this:
http://www.cocoanetics.com/2010/03/how-to-shrink-cells/

An efficient way of redrawing a UITableViewCell once its data is available

I currently have one UITableViewController that contains many cells with simple data in it. When the UITableViewController instantiates a UITableViewCell, it begins running a background thread for each individual cell to get its state from the server. The UITableViewCell is subclassed and already has a UIImageView property that needs to get added to the contentView once the data from the server is ready.
My problem is that is that I need to call [tableView reloadData] everytime I get new data back from the background threads. This is a bit overkill, as I can simply add the UIImageView to the contentView by accessing the affected cell directly. I'm just not sure of the best way to find the cell for when my data management utility is done doing its work (speaking to the server).
Does it make sense to do something such as passing the indexPath of the cell when calling my data manager to run its background task and then passing that indexPath back when the task is done? Is that overkill?
What's your favorite way for handling this common task?
Thanks SO crew.
In the case where the data needs to change inside of the cells I do not use the dequeueReusableCellWithIdentifier:(NSString *)identifier method. That stops the UITableView from caching the cell. When table view cells scroll off of the screen they are released. There is a delegate method,
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
that gets called just before your cell will be drawn. You can use this to, yes pass the indexPath to the manager.
When your data comes back, I usually call the method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
to get the cell, then set the text in the cell and call its UIView superclass method,
- (void)setNeedsLayout.
That will tell the cell to redraw.
Another way I have done it is to use a custom cell, and have that custom cell subclass call the manager for its data directly when it is drawn. When the data comes back, it calls a custom delegate method on itself to update its internal UILabel objects and then calls setNeedsLayout.
If you don't mind having a placeholder in your cell while the image loads, you can use something like TTImageView in the Three20 library. The image view is always in the cell and you specify its image URL. The image view handles the URL request, and when the image is loaded, it's automatically displayed.
While I don't use Three20, I do something very similar in my code. This is compatible with table cell reuse identifiers -- when a cell scrolls off the screen, its image view URL is changed to a new value, which cancels the URL request (if it's still ongoing) and starts a new one. Combined with caching, when you scroll back up the image will load instantaneously.