Re: Resizing UITableView will keeping content scrolled to bottom
Re: Resizing UITableView will keeping content scrolled to bottom
- Subject: Re: Resizing UITableView will keeping content scrolled to bottom
- From: Rick Mann <email@hidden>
- Date: Tue, 17 Apr 2012 14:14:05 -0700
Thanks for the lengthy explanation, Luke. I was aware of most of this. It seems like the result I'm seeing is a consequence of a number of things. Since the end result is correct, it seems like it should be possible to do things the way I was attempting to do them, but that subtleties in the implementation prevent it from behaving correctly.
I tried adjusting the content inset bottom instead, as you suggested, but unfortunately this required me to also move the text entry UI separately.
Then I tried just moving the entire container (table view plus message composition area) up, and adjusting the contentInset.top appropriately. This works correctly.
While it's a pity I can't just do the natural resize-frame-and-adjust-offset, I appreciate you giving me a workaround that gives me the results I need.
--
Rick
On Apr 17, 2012, at 10:48 , Luke Hiesterman wrote:
> So, attempting to modify both the frame and the contentOffset in the same animation is a problem. This has to do with how UIView animations work coupled with how UIScrollView works. I'm going to give a lengthy explanation of why this doesn't work, but you can skip to the bottom for what should be the solution.
>
> First a quick note on how UIView animations work (to make sure we're on the same page):
>
> 1. When an animatable property is modified within an animation block, UIKit sets up an animation on the given view's underlying CALayer object. That animation has a fromValue and a toValue which define what we're animating from and to. When you set a property, the value you set gets taken as the animation's toValue, and whatever was already there gets set as the fromValue. Thus, if you have a view whose alpha is 0.0, and you set its alpha to 1.0 in an animation block, UIKit sets an alpha animation on the CALayer whose fromValue is 0.0 and whose toValue is 1.0. An interesting caveat here is that if you set the same property twice in the same turn of the runloop, the animation results might not be as you expect, because the fromValue used by UIKit is always the model value just before you make the change. Thus if we start with a view whose alpha is 0.0 and run this code
>
> [UIView animateWithDuration:0.3 animations:^(void) {
> view.alpha = 1.0;
> ....
> view.alpha = 0.9;
> }];
>
> The result will be that the view jumps immediately to alpha 1.0, and then animates to 0.9, even though the 1.0 was in the animation block. The second animation clobbers the first one and uses the model value set by the first animation as its fromValue.
>
> Moving on....
>
> 2. Frame changes are interesting because a frame is not directly a property of a view - it is a derived property of its center, bounds, and transform. When you modify a view's frame, you are actually modifying its center and bounds, which are each distinctly animatable properties, and UIKit sets up animations on the underlying CALayer for each of them.
>
> And we also need to make sure we're on the same page about how scrolling works in UIScrollView:
>
> 3. The portion of any view that is drawn on screen is defined by its bounds - the coordinate space of that view. Normally, a view has a bounds.origin of 0,0 and a bounds.size equal to its frame.size. A UIScrollView brings new content onto the screen, getting the effect of scrolling, but modifying its bounds.origin. In fact, contentOffset is really just bounds.origin.
>
> Ok, so bringing this all together....
>
> When you change a table view's contentOffset and its frame.size in the same animation, you've actually clobbered one of your animations, because both of those properties ultimately result in a bounds animation on the underlying layer. One is modifying the bounds origin, and the other bounds size, but they're both just bounds animations, and just as we saw in the alpha case above, the second one will stomp on the first one and use the first one as its fromValue. Thus you will see things "jump" to the first bounds value you set and then animate to the second.
>
> And now what should be a solution to the long-winded explanation of the problem:
>
> Instead of changing the frame of a table view to accommodate the keyboard coming up, we recommend changing the contentInset. By adding the height of the keyboard to the table's contentInset.bottom, you get additional scrolling area so things don't get stuck behind the keyboard, but you won't be creating a bounds animation in the process. In fact, UITableViewController does this automatically for table views that it manages.
>
> Hope that helps.
>
> Luke
>
> On Apr 16, 2012, at 6:27 PM, Rick Mann wrote:
>
>> Er, for reference, the view hierarchy is this:
>>
>> http://latencyzero.com/stuff/ViewHierarchy.png
>>
>>
>> On Apr 16, 2012, at 18:16 , Rick Mann wrote:
>>
>>>
>>> On Apr 16, 2012, at 16:32 , Luke Hiesterman wrote:
>>>
>>>> You can do this by wrapping the operation in your own animation block. This simple code demonstrates doing it on 44 point high rows:
>>>>
>>>> [UIView animateWithDuration:0.3 animations:^(void) {
>>>> [tableView beginUpdates];
>>>> CGPoint contentOffset = tableView.contentOffset;
>>>> if (contentOffset.y > 0) {
>>>> contentOffset.y += 44;
>>>> tableView.contentOffset = contentOffset;
>>>> }
>>>> [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:__numRows inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
>>>> __numRows++;
>>>> [tableView endUpdates];
>>>> }];
>>>
>>> Yeah, this is essentially what I do, but while I can correctly animate the frame change alone, if I try to do that AND change contentOffset, it doesn't work.
>>>
>>> Please see the following videos. For reference, the view hierarchy is this:
>>>
>>> http://latencyzero.com/stuff/AdjustingOffset.mov
>>>
>>> The parent View is a blue color. The Container view is green. The UITableView is pink.
>>>
>>> If I do not adjust the content offset (that is, if it gets set to 0.0), you can see the views move and resize correctly:
>>>
>>> http://latencyzero.com/stuff/AdjustingOffset.mov
>>>
>>> If I DO adjust the content offset (even if I hard-code it to 10 pixels), everything ends up in the right place, but the table view immediately resizes to the proper height, but the frame.origin.y is adjusted about 81 pixels down in the view. It snaps to this position, THEN animates to the correct position.
>>>
>>> http://latencyzero.com/stuff/NoOffsetAdjustment.mov
>>>
>>> The code that does this (for the keyboard appearing) is here:
>>>
>>> http://pastebin.com/zRSR78fZ
>>>
>>>>>
>>>>> 2) When animating a frame change, are subframe re-sizes also animated? It looks like they're partly immediately update, then animating.
>>>>
>>>> Any subviews which are resized in the scope of the superview's frame change will share the animation, which includes anything that has autoresizing masks. You may need to invoke -layoutIfNeeded within your animation block on views who defer resizing of their subviews until layout time to capture some things in an animation. But that discussion is orthogonal to your stated goal, which can be achieved by following the sample I've provided above.
>>>
>>> I tried throwing in a -layoutIfNeeded, but it had no effect.
>>>
>>> --
>>> Rick
>>>
>>>
>>>
>>>
>>> _______________________________________________
>>>
>>> Cocoa-dev mailing list (email@hidden)
>>>
>>> Please do not post admin requests or moderator comments to the list.
>>> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>>>
>>> Help/Unsubscribe/Update your Subscription:
>>>
>>> This email sent to email@hidden
>>
>
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden