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 protected])
>>>
>>> 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:
>>> https://lists.apple.com/mailman/options/cocoa-dev/rmann%40latencyzero.com
>>>
>>> This email sent to [email protected]
>>
>
_______________________________________________
Cocoa-dev mailing list ([email protected])
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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]