Split view doesn't resize subview properly during a toggle uncollapse after manual collapse
Reported by Brandon Walkin | August 30th, 2009 @ 04:10 AM
BWSplitView doesn't resize a subview properly during a toggle uncollapse after a manual collapse.
There was a fix committed to the repo that has the divider moving to the correct position in this scenario, but the subview of the collapsible pane doesn't resize to the correct size.
Comments and changes to this ticket
I've isolated the issue to the code added in this changeset: http://bitbucket.org/bwalkin/bwtoolkit/changeset/e45b8c434e32/
I think I found the fix for this (meaning it works for me)
You have this in BWSplitView.m in the toggle method:
// Turn off autoresizesSubviews on the collapsible subview [[self collapsibleSubview] setAutoresizesSubviews:NO];
I set it to YES instead of NO and it works flawly now.
Indeed I saw this happen. So I disabled horizontal scroller in my app's sidebar.
Again I didn't say it was a general solution but I don't see the original problem of bad restoration of subviews.
By the way, in my testing, not only did it restore badly after manual collapse. Occasionally it would also resize badly after programmatic collapse/uncollapse. But I didn't find any clue as to why it was sporadic.
I was chasing this bug in the hopes of even finding a specific solution to our needs. I think I did discover the source of the sporadic resize issues when responding to the toggleCollapse: method (as Jacob G. mentions above). The animations are set to run for a specific duration. You then call performSelector with a delay that's the same as the animation duration. Since my views are somewhat complex, I wondered if a race condition was ensuing.
I suspect that the timer for the three calls at the bottom of toggleCollapse (animationEnded, restoreAutoresizesSubviews, resizeAndAdjustSubviews) may be firing before the animation has completed. As a test, I simply added 0.1 to the delays for those three calls and I haven't seen that problem in my app again.
This still feels fragile, but I can't find a way to get a callback from the NSAnimationContext the same way you can from a CAAnimation. I'd be more comfortable if the order of execution was deterministic rather than dependent on timing, but this will work for me for now.
Having run into this same issue, I think it's exactly as Wyatt says; the autoresizesSubviews is restored while the animation is still active. The last steps of the animation therefore tend to drag the subview in the same direction as the animation, and the new position is then preserved (until the bug kicks in again).
I see two possible solutions: one being increasing the delay on the timers slightly (hacky, but easy and should work the vast majority of the time), and the second is to retrieve the CAAnimation, set the delegate, and use that to implement a animationDidStop:finished: callback - which is clean but much more convoluted...
I have a similar issue. I don't know if this is a bug, and if it's a bug it may be the same thing described in your comments.
I put some views and controls in a panel which collapses (manually and by clicking a button). When collapsing is done manually (when everything is done manually) or when everything is done by clicking the collapse button, everything goes fine.
When I collapse manually and then I click the collapse button, the view restores its size correctly, but its content disappears. Is this a bug or I am doing something wrong?
Apologize for bad english; I think this is the best place to write, otherwise please delete my comment.
I've been chasing this bug and found out exactly why it is happening:
Using the toggleCollapse: turns off autoresizing of subviews to prevent scrollbars from showing up. This is a good thing since it wouldn't look good for these to show up as it's animating.
Toggling a collapse from dragging the divider index does resize the subviews and the resulting subview's frame widths are 0.0. Therefore if you use toggleCollapse: to uncollapse a view after manually collapsing it the view's frame width's are 0.0 and since toggleCollapse: turns off the auto-resizing you are left with 0.0 width frames. This hides them and only will show them as you drag the divider more and makes it look really buggy.
MyContainerView is the collapsable view that is part of BWSplitView MyContentView is a scroll view with content inside of MyContainerView and has all auto-resizing masks set so it will always 'fill' MyContainerView
- User drags divider index small enough that BWSplitView collapses MyContainerView at this point MyContainerView width is 0.0 and because MyContentView had all the resizing flags on it is now also 0.0.
- User clicks a button triggering "toggleCollapse:" BWSplitview turns off autoresizing and animated MyContainerView back to the size it was prior to the collapse. MyContainerView's width is now correct but MyContentView is still 0.0 since autoresizing was turned off.
The solution is rather complex but also extremely simple. Every time BWSplitView records uncollapsedSize we also want to snapshot all sizes of the subviews inside collapsibleView. When restoring the collapsibleView from animation we then simply restore all the sizes from the uncollapsedSize.
I've attached a new BWSplitView.m file that does such. Look specifically for the saveUncollapsedSizes and restoreUncollapsedSizes methods. It also include the small delay fix noted above to avoid race conditions.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
BWToolkit is an Interface Builder plugin that contains commonly used UI elements and other objects designed to simplify Mac development.