Confusion about UIView's setNeedsDisplay - iphone

I have an ivar of UITableView* myTableView. I have data being loaded from internet so when the data gets loaded should i call [myTableView setNeedsDisplay], i know i can/should call [myTableView reloadData] but the confusion is that calling setNeedsDisplay should also work but is not working.
I am just looking for an explanation as to what setNeedsDisplay does? when called on UITableView instance.
As per the documentation setNeedsDisplay calls drawRect in "next drawing cycle". so can anyone tell me what is the next drawing cycle.

setNeedsDisplay method has nothing to do with the reloading of the tableView. It just redraws the view of the tableView, not its cells or separators.
setNeedsDisplay and reloadData are entirely for different purposes.

setNeedsDisplay marks the calling UIView as needing to be redrawn is a purely visual sense, which as stated will happen on the next drawing cycle.
This is completely different to reloading the data when used in a UITableView context.
See this article for a little more information on the subject.

SetNeedsDisplay
A good example of this is when a UIImageView is rendered with one image; and while it is still on screen, you change the image to something else. In this case, just changing the image will not always trigger a repaint of the view, for that to occur you need to make a call to setNeedsDisplay.
reloadData
It has to be called when the underlying data source for the tableview is changed and you want that to be refelected in UI.In that case call this reloadData mehod

Generally, you only need to call setNeedsDisplay on custom views for which you have implemented drawRect.

My solution was to update all visible cells and keep track of indexPath of cell in my custom class "ProductTableViewCell" with reloadRowsAtIndexPaths, see below:
NSArray *arrayCells = [self.tableView visibleCells];
NSMutableArray *mArrayIndexPaths = [[NSMutableArray alloc] init];
for(ProductTableViewCell *cell in arrayCells) {
[mArrayIndexPaths addObject:cell.indexPath];
}
[self.tableView beginUpdates];
[self.tableView endUpdates];
[self.tableView reloadRowsAtIndexPaths:[mArrayIndexPaths copy] withRowAnimation:UITableViewRowAnimationAutomatic];
Here is how the class is defined:
#interface ProductTableViewCell : UITableViewCell {
NSString *reuseID;
}
#property (nonatomic, strong) NSIndexPath *indexPath;

Related

tableView:didSelectRowAtIndexPath:]: message sent to deallocated instance 0xebba1b0

I understand the reason why message sent to deallocated instance 0xebba1b0 is called, it is because I am sending a message to an object which is no longer in memory.
So here's my scenario. I have a ZoomedViewController which has a UITableView in it. The UITableView has a custom UITableViewCell, which has an attributed label as a subview. When a link is pressed on the attributed label (which in turns triggers didSelectRowAtIndexPath) it delegates to my MainViewController and calls the method closeZoomedImageVC in MainViewController:
-(void) closeZoomedImageVC
{
[self.zoomedImageContainer_ removeFromParentViewController];
[self.zoomedImageContainer_.view removeFromSuperview];
}
the issue is that when that didSelectRowAtIndexPath is triggered, then zoomedImageContainer_ is already gone. How do I solve this then?
To illustrate the point better, basically when I do:
[self performSelector:#selector(closeZoomedImageVC) withObject:nil afterDelay:1.0];
this doesn't cause the crash anymore, but this is not a solution as it is hacky. What this does is it lets didSelectRowAtIndexPath to be executed first before it is deallocated.
Store a reference to your UITableView in ZoomedViewController:
#property (nonatomic, strong) IBOutlet UITableView *tableView;
Make sure to connect the outlet in Interface Builder. Now, when your zoomedImageContainer_.view is removed, it won't dealloc the UITableView until you release that reference as well.
You also need to store a strong reference to your ZoomedViewController in MainViewController, and only set that to nil after you have saved the selected row back in MainViewController.
When tapping on a cell in a table will invoke an animation to highlight selected cell, and it needs a short duration complete, so the best way is what you are doing, perform selector after a delay, I think 0.5 second is enough.
I found the solution to my self is to just set the allowSelection = NO in the tableView property. This will let the attributedLabel inside the UITableViewCell to have interaction but will disable didSelectRowAtIndexPath being called

Custom UITableViewCell woes

I have an iPhone app with a form input screen. I did this by making a custom UITableViewCell that has a UILabel and a UITextfield. I set it up so that the "cellForRowAtIndexPath" fetches the appropriate value from Core Data, and the UITextField's "textFieldDidEndEditing" method saves the appropriate value to Core Data. It works great... except:
If I edit a text field, then scroll it off screen, then click on another cell's text field:
The cell has been autoreleased because it scrolled off screen
The "textFieldDidEndEditing" gets a BAD ACCESS error
I understand the problem completely, I'm just not sure the best way to fix it. My first thought was to add the logic from "textFieldDidEndEditing" to "dealloc", but that seems hacky. Any suggestions?
I ended up using a delegate method for scroll view (which is built in to the UITableView). When the user starts dragging, I resign first responder.
This works perfectly because it looks nice, and "textFieldDidEndEditing" gets called when the user starts to scroll, which is always before the text field goes off screen.
disable scrolling while editing
retain your textField
that are the things you could do. In my opinion its best to disable scrolling while editing because the user has no need to, so make sure he also can not do so. Limit the things your user can do, makes it more "secure" for you and easier to use for the user.
If you are not sure about such things just look at what apple does, they are always right in their applications. Like in the settings app on iOS 5, when you change your phone's name. You simply get 1 row in the next tableview so you can't really mess anything up as the user..
In your custom cell's dealloc, set the textfield's delegate to nil
I had a similar issue... The problem lies in the reusable cells as part of the Table View. Every time the table is scrolled, the cellforRowatIndexPath gets called, and dequeues a cell and returns it. Sadly, this functionality doesnt work well with cells having retainable data such as labels. Hence you must opt for your own method of dequeueing the cell.
I have an NSMutableArray called cells which holds all my cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [self dequeuwReusableCellwith:indexPath];
if (cell == nil)
{
cell = [[CustomTableViewCell alloc] init];
[cells addObject:cell];
}
// Configure the cell.
NSString *temp = [NSString stringWithFormat:#"Cell %d",indexPath.row];
cell.textField.placeholder=temp;
return cell;
}
and this is my custom method to deque Reusable cells.
-(CustomTableViewCell*)dequeuwReusableCellwith:(NSIndexPath*)indexpath
{
if([cells count]>indexpath.row)
{
return [cells objectAtIndex:indexpath.row];
}
return nil;
}
Hope this helps...

Alloc - add as accessoryView - release: does it leak?

Does this leak memory? This code is executed in cellForRowAtIndexPath: outside the cell creation block (so each time the table cell is updated).
MyView *myView = [[MyView alloc] init];
// ... configuration code
cell.accessoryView = myView;
[myView release];
Or in other words, will the UITableViewCell release the object in its accessoryView when a new object gets assigned to it?
Thanks.
Yes, the cell will release the accessory view and you do not have a leak in the example.
The property accessoryView of a UITableViewCell is a retain type, in common with many view properties in the kit. Check the Apple documentation for UITableViewCell to convince yourself of this. Therefore there will be no leak in your example - the retain count has been correctly managed. You've also correctly released after setting the accessory view, on account of your alloc call.

Open UITableView after Click on Button

I have a Tabbar - Application and i want to show data (NSString) from a NSMutableArray in an UITextfield or better in an UITableView after a button-click.
how can i init and open and fill the UITabelView?
regards
That seems like a basic implementation of a UITableView. The Apple UITableView documentation should cover any questions you have on UITableViews. For your button click, tie the IBAction of the button to call [yourTableView reload]. This forces a load of the table view.
Are your tableView and textField in the same tab where your button is?
Say you have stringArray which is holding all the NSString Objects.
then you can set
textField.text = [stringArray objectAtIndex:indexYouWant];
If you want to add the whole array to tableView, you will need to use that stringArray and implement tableView Methods.
But still I am not clear with your question.
This is an overly broad question. Why not look at some example code and try to figure it out? If you get stuck, come back and post a question and some code showing where you are having a problem.
You need to know the Life Cycle of UIViewController and its UIView
Concentrate on the methods that are responsible for the life cycle of the UIViewControllera:
Creature
Init
initWithNibName:
Creating view
(BOOL) isViewLoaded
LoadView
viewDidLoad
(UIView *) initWithFrame: (CGRect) frame
(UIView *) initWithCoder: (NSCoder *) coder
Processing state change view
viewDidLoad
ViewWillAppear: (BOOL) animated
ViewDidAppear: (BOOL) animated
ViewWillDisappear: (BOOL) animated
ViewDidDisappear: (BOOL) animated
ViewDidUnload
Processing memory warning
DidReceiveMemoryWarning
Destruction
ViewDidUnload
was given
Proceeding from this in one of the initialization methods after clicking on the button, you must fill out the table and call after the reloaddata

Change Height of UITableViewCell from within the UITableViewCell inherited class when UITextField receive focus

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.