LayoutSubviews for Custom UITableViewCell and UIView - iphone

I have a custom UITableViewCell. It has a UIView which is added to the "contentView" of the UITableViewCell. For any update, I'm redrawing that UIView by calling its "setNeedsDisplay" and implementing drawing inside "drawRect" method of the UIView.
The UITableViewCell overrides "willTransitionToState" and according to the bit mask value, asks the UIView to redraw.
Because I'm asking the UIView to redraw itself again, every time I do a "swipe to delete", I see the cell "flicker" a moment; even the text that didn't move position due to the Delete button suffers from a flicker since everything is being redrawn.
I'm aware that a possible solution is not to call "setNeedsDisplay" of the UIView from the "willTransitionToState" but instead call "setNeedsLayout" and have the UIView implement "layoutSubviews".
This is where I'm stuck at: how can I re-layout my UIView since everything inside my UIView is "drawn" (I use "drawInRect" and "drawAtPoint" methods for strings and images). There is also a string on the right side that I wanna hide when the "Delete" button appears (like in the Messages app in iPhone).
How can I do this by doing re-layout instead of re-draw?
Thank you!!!

I think there's an issue with your approach. Rather than draw everything, it's better to set up your subviews in an init method, or in the NIB.
In the willTransitionToState method, update whatever subviews according to the state transition.
In layoutSubviews, update each subview's origin and size as required.
Here's some detail from the willTransitionToState documentation. Although, I'm sure you'd have seen this already:
Subclasses of UITableViewCell can implement this method to animate additional changes to a cell when it is changing state. UITableViewCell calls this method whenever a cell transitions between states, such as from a normal state (the default) to editing mode. The custom cell can set up and position any new views that appear with the new state. The cell then receives a layoutSubviews message (UIView) in which it can position these new views in their final locations for the new state. Subclasses must always call super when overriding this method.

Related

Where to build custom UITableViewCell: in layoutSubviews or initWithStyle:reuseIdentifier?

As I understand, there seem to be three ways of laying out a custom table view cell:
In Interface Builder.
In layoutSubviews.
In initWithStyle:reuseIdentifier:.
What's the difference between the second and third methods? Also, why not drawRect?
P.S. I'm following Chapter 9 of the book Pro iOS Table Views by Tim Duckett, where we're building custom UITableViewCells. The author shows how to lay out the cell in the same order as above, but I don't know whats the difference between the last two since they both do away with IB. I noticed, though, that the author simply sets the frames in layoutSubviews unlike in initWithStyle:reuseIdentifier: where properties concerning the look of a view are set (e.g., background image, background color, etc.).
You should create your cell subviews in
- initWithStyle: (UITableViewCellStyle) style reuseIdentifier: (NSString*) resueIdentifier
And layout them in layoutSubviews.
-initWithStyle:reuseIdentifier method will be called once at initialization, but you cell may have incorrect frame at this moment.
Thats why you need to layout it in layoutSubviews method, it's called when cell needs to update layout, for example, when autorotation occurs. So if you will layout subviews in this method, you will have valid cell's frame and correct subviews layout.
-[UIView layoutSubviews]
gives you much more control on the way a view is relayout (I suppose in your book example it is done in the table cell contentView).
When you use a NIB file what you do is simply to recreate in memory the view hierarchy with some properties set, but the nib has not the dynamic capability to relayout a cell (or it is limited to the spring/struct or auto-layout capabilities). If you don't use the NIB you must create this hierarchy programmatically.
Especially in a table view cell subclass, you can add to it many properties that could change the way the cell is relayout, e.g. showing a thumbnail image or not, display a certain label or not, and in such cases you probably you need the layoutSubviews method to update the cell layout taking into account all the possible layouts due to different properties values.

using a subclass of UITableViewCell in IB - Why is there a need to override drawRect:?

https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7-SW12
In relation to subclassing UITableViewCell, the above link talks about the need to override drawRect: to explicitly draw the cells content.
But why is this necessary? Can we not just create a subclass of UITableViewCell in IB, add the required subviews in IB, create the required outlet connections and then set the values of these subviews programmatically.
Why is there a need to override drawRect:?
You're not required to override drawRect: any more than you are required to use the textField implementation methods for UIAlertView. Drawing the cell's content, however, can boost performance in some cases, particularly if you have multiple non-opaque layers and can draw them into one opaque cell, because the renderer doesn't have to composite and blend multiple times. By drawing into a single opaque layer, the hardware only has to blend right before it gets displayed on screen.
Note that if you don't experience lag or if you have animation in each cell themselves, then drawing will not help you.
I can't find in your link where it says you are supposed to subclass DrawRect method.
I don't think apple recommand it.
I have developped an entire app with a lot of tableControllers with custom tableViewCells, using nib files and never subclassing drawRect method.
I never noticed any problem, and my app passed successfully the app store review.

Touch on UIButton in a UITableViewCell cannot scroll the table

I need to have a UIButton inside a UITableViewCell, everything works fine except when I touch inside the button and move my finger, the table does not scroll.
I tried to subclass UIButton and in touchesMoved: method send the same message to self.nextResponder, or call touchesCancelled and hope it will pass the touch event to next responder, they both do not work.
Is there any good way to solve this problem? Currently I am adding a UIView on top of the UITableViewCell, and detecting touch events manually, passing result to the button or the rest of the cell respectively, but this is a little bit dirty.
What you can try is setting the delaysContentTouches property of the UITableView to YES.
Additionally you can set the canCancelContentTouches to YES.
If the value of this property is NO, the scroll view does not scroll
regardless of finger movement once the content view starts tracking.
Source: UIScrollView Class Reference
Try:
_yourTableView.delaysContentTouches = YES;
_yourTableView.canCancelContentTouches = YES;
The best way is to make a custom cell and do your work neatly.
Check the Apple docs

Why does my UIView subclass not redraw after I call setNeedsDisplay?

I have a custom UIView which is a sub-view of a UITableViewCell. When the cell is drawn, so is my custom view, correctly calling drawRect: in my UIView sub-class. When the cell is reloaded, the view is drawn correctly (drawRect: is called).
When a certain event happens (eg timer), I want to redraw the view without having to reload the whole cell. (This is not the only view in the cell.)
However, when I call setNeedsDisplay on my sub-view, nothing happens.
I'm guessing that when my view's drawRect: is called, the resulting image is cached in a backing somewhere such that when I call setNeedsDisplay that backing is redrawn to the screen, but my drawRect: call is never called again.
Why is drawRect: never called again after the cell is created?
EDIT: The cell's subviews are created from from a nib. ie: a UIViewController is loaded from the nib and its view is added to the UITableViewCell.
I made a test case from scratch with a custom view that isn't loaded from a nib (created by code) and it works as expected: setNeedsDisplay results in a drawRect: call. It must be something that's either set up in the .xib file or something that happens differently to a view when it's loaded from a nib.
I just ran into the same problem myself. For me it was an issue with my XIB - I am subclassing a UIView though - so adapt accordingly. My mistake in IB was that I attached my controls to the "file's owner" instead of attaching them to the UIView. I'm sure that in my inexperience there is something larger going on - perhaps someone with a bit more experience can elaborate.
Hope that helps!

determine if uiview is displayed

is there a possibility to determine if an uiview obj is going to be displayed. imagine: you have 2 uiviews in an uiscrollview. now you are going to switch per gesture from the first view to the second. the first view now is NOT in the viewport. now you are going to go back to the first view. and now I want to be notified that this view is in viewport, or is redisplayed. the same has to be for the second view. I have not found any callback or something like this.
You make sure your UiViewController overrides viewWillAppear: (before it appears this method is called) or viewDidAppear: (after this method is called).
See: http://developer.apple.com/iphone/library/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/viewWillAppear:
That depends what you mean by "switch". If one view is just scrolled out of the visible area of the scrollview, but still remains attached as a subview to it, then you may want to check if the bounds of your view overlap those of the scrollviews visible area.
You could do this by using UIScrollView Delegate's scrollViewDidScroll: method to implement a check for overlaps while the user is scrolling.
If however your view is actually removed from the viewstack, then you may want to subclass UIView and implement willMoveToSuperview: to check if the view has been added to the scrollview again.