NSSplitView question - how to implement my own "adjustViews" style method

  • Hello everyone.

    I have an NSSplitView with dynamic contents (views can be added and deleted in runtime). Views can also be collapsed by the user, or programmatically at certain situations.

    My problem is this. Each of my views has a minimum size and a maximum size, that must be met anytime, or the content gets flawed.

    Implementing

    - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex;
    - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex;

    I was able to achieve that -- When the user drags the dividers between subviews of the NSSplitView.

    However, when the NSSplitView itself is resized (following a resize of its window or superview), the normal behavior of NSSplitView is not to respect my provided constraints. It calls on the internal implementation of the delegate method

    - (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize;

    Which in turn calls the internal implementation of

    - (void)adjustSubviews;

    who tries to adjust the frames of subviews of the splitter, maintaining their proportions, but NEGLECTING the min/max size constraints supplied by the delegate.

    I Implemented my own logic into the splitView:resizeSubviewsWithOldSize: delegate method, trying to maintain both proportional scaling AND min/max constraints, but I reach a situation where the overall splitView is too small to show all of the subviews --- even in their minimal sizes.

    At that point, I need to COLLAPSE one of the subviews, to keep the constraints. However I must do this  from WITHIN the splitView:resizeSubviewsWithOldSize: implementation, which sets up the frames for all subviews! Meaning --- to so manipulate the subview I want to collapse, that NSSplitView will "Understand" that it is collapsed. when the method returns.

    In other words, I need to imitate what adjustSubviews does and what NSSplitView does, to collapse a subview.
    It is not fully documented --- how NSSplitView actually collapses a sub-view, but there are several hints.

    1. I know a collapsed subView gets hidden. (isHidden == YES).
    2. I know the the collapsed subview usually remains with its original frame, despite NSSplitView documentation saying ---

    "Collapsed subviews are hidden but retained by the split view. Collapsing of a subview will not change its bounds, but may set its frame to zero pixels high (in horizontal split views) or zero pixels wide (vertical)."

    3. The above insinuates, that NSSplitView sets up Bounds transform on its subviews --- but this s nowhere in the  documentation, and it is not explained what happens if the subview already had its special transform then NSSplitView wants to collapses it.

    4. There is NO API to programmatically collapse a subview of the NSSplitView. It is almost ridiculous not to have such API. I tries several ways, and the one that most closely works for me is to set the subview's divider position past its min/max position, to get to to collapse --- however, Here (in resizing the SplitView) I cannot use this.

    Can anyone enlighten me on the subject? Did anyone ever shed his eyes on Apple's implementation of adjustSubViews, or other parts of NSSplitView?

    I really need an advice here.

    Thanks (and sorry for the lengthy question).

    Motti Shneor.
  • On 03/07/2012, at 12:21 AM, Motti Shneor wrote:

    > I really need an advice here.

    This will sound flippant but it's not meant to be: implement your own split view.

    NSSplitView is the most bizarre piece of design and difficult to get to behave just how you want even in simple cases like having two views, one which should stay fixed when the overall view is resized. This behaviour is likely to be the most commonly needed but is not the default for NSSplitView. The delegate methods are weird and don't get called in every case. Once you add in the complexity of dynamic views, your head is going to be spinning.

    Rolling your own is not that hard, and can be designed with your use-case in mind.

    --Graham
  • Thanks Graham (Sigh…)

    I was beginning to think I'm stupid or something, struggling so hard with a UI element as ordinary as a Split-View.

    I have the feeling I "almost got it", and I even think I understand why and when delegate methods are being called.  Rolling out my own SplitView doesn't seem to be easier than finding the answer to my question, because to inherit from NSSplitView I'd still need to understand how "super" works won't I?.
    Also I'll have to re-implement lots of behavior (animation, dynamics, efficient drawing etc.) that might prove a big job.

    I could re-state my question again even simpler --- I want to know what makes NSSplitView respond with "YES" to the following:

    [mySplitView isSubviewCollapsed:panelSubview];

    Is it just the subview being hidden? being zero-framed? being vertically/horizontally transformed to zero? Does the NSSplitView maintain a "collapsed" state member for each of its subviews? How can I set this state?

    and if anyone from Apple is on this list --- for god sake, why isn't there a [mySplitView setSubview:panelSubview collapsedStateTo:YES/NO] ????

    On 3 ביול 2012, at 03:14, Graham Cox wrote:

    >
    > On 03/07/2012, at 12:21 AM, Motti Shneor wrote:
    >
    >> I really need an advice here.
    >
    >
    > This will sound flippant but it's not meant to be: implement your own split view.
    >
    > NSSplitView is the most bizarre piece of design and difficult to get to behave just how you want even in simple cases like having two views, one which should stay fixed when the overall view is resized. This behaviour is likely to be the most commonly needed but is not the default for NSSplitView. The delegate methods are weird and don't get called in every case. Once you add in the complexity of dynamic views, your head is going to be spinning.
    >
    > Rolling your own is not that hard, and can be designed with your use-case in mind.
    >
    > --Graham
    >
    >

    Motti Shneor,
    CEO,  suMac LTD.
    Software Development for the Macintosh

    Home/Office Address: 34 Emek-Ha-Ella St. Appt.1 Modiin, ISRAEL, 71723
    Home/Office Tel/Fax: +972-8-9267730
    Home eMail: <motti.shneor...> Office eMail: <sumac...>
    Mobile phone: +972-54-3136621
    ---
    ceterum censeo microsoftiem delendam esse
    ---
  • Thanks Graham (Sigh…)

    I was beginning to think I'm stupid or something, struggling so hard with a UI element as ordinary as a Split-View.

    I have the feeling I "almost got it", and I even think I understand why and when delegate methods are being called.  Rolling out my own SplitView doesn't seem to be easier than finding the answer to my question, because to inherit from NSSplitView I'd still need to understand how "super" works won't I?.
    Also I'll have to re-implement lots of behavior (animation, dynamics, efficient drawing etc.) that might prove a big job.

    I could re-state my question again even simpler --- I want to know what makes NSSplitView respond with "YES" to the following:

    [mySplitView isSubviewCollapsed:panelSubview];

    Is it just the subview being hidden? being zero-framed? being vertically/horizontally transformed to zero? Does the NSSplitView maintain a "collapsed" state member for each of its subviews? How can I set this state?

    and if anyone from Apple is on this list --- for god sake, why isn't there a [mySplitView setSubview:panelSubview collapsedStateTo:YES/NO] ????

    --Motti

    On 3 ביול 2012, at 03:14, Graham Cox wrote:

    > On 03/07/2012, at 12:21 AM, Motti Shneor wrote:
    >
    >> I really need an advice here.
    >
    > This will sound flippant but it's not meant to be: implement your own split view.
    >
    > NSSplitView is the most bizarre piece of design and difficult to get to behave just how you want even in simple cases like having two views, one which should stay fixed when the overall view is resized. This behaviour is likely to be the most commonly needed but is not the default for NSSplitView. The delegate methods are weird and don't get called in every case. Once you add in the complexity of dynamic views, your head is going to be spinning.
    >
    > Rolling your own is not that hard, and can be designed with your use-case in mind.
    >
    > --Graham
  • On Jul 2, 2012, at 9:46 PM, Motti Shneor wrote:

    > Thanks Graham (Sigh…)
    >
    > I was beginning to think I'm stupid or something, struggling so hard with a UI element as ordinary as a Split-View.

    I ran into this exact same problem last week. I can't believe it is an extremely rare situation.

    I tried using constraints to solve it, but I'm such a newbie at constraints I didn't think I had a chance in the near term. I did see this example code, but I haven't tried it:

    http://developer.apple.com/library/mac/#samplecode/Cocoa_Autolayout_Demos/L
    istings/ReadMe_txt.html#//apple_ref/doc/uid/DTS40010636-ReadMe_txt-DontLink
    ElementID_13


    > "SplitView" demonstrates the API from the perspective of a view.  It implements a split view with one extra feature: when a divider is dragged, it pushes other dividers out of the way if necessary, and they snap back.  The class is 245 lines long compared with NSSplitView's 2502. This is of course not a fair comparison because NSSplitView does more, but it's clear that it's a lot simpler, particularly in the dragging code.  This example illustrates fancy use of priority, overriding updateConstraints, dragging, and constraints that cross the view hierarchy.

    Todd
  • On Jul 2, 2012, at 21:46 , Motti Shneor wrote:

    > for god sake, why isn't there a [mySplitView setSubview:panelSubview collapsedStateTo:YES/NO] ????

    Well, one possible answer is to ask yourself if you're asking the right questions.

    I think there's perhaps a small difference between the user collapsing a divider, and the split view eliminating one of its subviews because there isn't enough space. The two things are similar "geometrically" but not very similar in terms of usability.

    For example, if I collapse a divider, then resize the view larger, I wouldn't expect the collapsed divider to auto-uncollapse. But I do expect a suppressed subview to reappear as soon as there's room. So perhaps you shouldn't be trying to collapse what won't fit, but to hide it.

    Or, from a different perspective, you might want to ask (by which I mean try as an experiment, rather than just think about) whether users might actually be happier with a split view that scrolls horizontally (or whatever direction you're squeezing it) rather than losing parts of itself. I understand why you might expect scrolling to be a terrible solution --and it might really be a terrible solution -- but it may be less confusing than having things popping into and out of existence.

    Or, from yet a different perspective, the fact that you're finding it difficult to implement the desired behavior might be an indication that you're designing UI behavior that's intrinsically too complicated.

    Finally, to put words into Graham's mouth, I don't think he was suggesting you subclass NSSplitView, but that you rewrite the whole behavior in a new class of your own. In that case, you don't need to understand anything about what NSSplitView does, you can just write what you want.
  • On 03/07/2012, at 2:46 PM, Motti Shneor wrote:

    > have the feeling I "almost got it", and I even think I understand why and when delegate methods are being called.  Rolling out my own SplitView doesn't seem to be easier than finding the answer to my question, because to inherit from NSSplitView I'd still need to understand how "super" works won't I?.
    > Also I'll have to re-implement lots of behavior (animation, dynamics, efficient drawing etc.) that might prove a big job.

    True in part. But you could subclass NSView instead. After all, NSSplitView doesn't really bring that much to the table - it relies on inheriting NSView to store its subviews, the only thing it draws is the actual splitter itself, which is  just a line (or narrow bar if you are using that style). In fact NSSplitView doesn't animate - if you want to make it do so, you have to add all the animation yourself (for example a panel sliding in and out of view). By the time you've got it bent to your will, you'll have written so much code in the delegate that you may as well have written a custom view class in the first place. It doesn't even give you a particularly easy way of setting the split position programatically.

    > I could re-state my question again even simpler --- I want to know what makes NSSplitView respond with "YES" to the following:
    >
    > [mySplitView isSubviewCollapsed:panelSubview];

    I think it's that the subview is hidden.

    > Is it just the subview being hidden? being zero-framed? being vertically/horizontally transformed to zero? Does the NSSplitView maintain a "collapsed" state member for each of its subviews? How can I set this state?
    >
    > and if anyone from Apple is on this list --- for god sake, why isn't there a [mySplitView setSubview:panelSubview collapsedStateTo:YES/NO] ????

    The subview isn't zero framed. Once upon a time it was, but that makes it an even bigger pain than usual to get it back into a sensible state if it contains further views that are autoresized. At some point, it was changed so that the view is maintained at its (minimum?) size and simply hidden. That's my current understanding anyway.

    Seems to me a sensible design for a split view would have simple properties like a min and max width for each subview and a sizing priority, and simple methods to show and hide each pane with or without animation. Maybe the newer constraints stuff helps but given that NSSplitView as it stands has to be backward-compatible with all the gnarly code that has gone before to make it work, it's always going to be awkward. It's surprising Apple persist with this class - they should implement a complete replacement which is not a subclass and then new code can adopt it without breaking old code.

    Just my 2¢ worth.

    --Graham
  • On Tue, Jul 3, 2012, at 03:22 PM, Graham Cox wrote:
    > True in part. But you could subclass NSView instead. After all,
    > NSSplitView doesn't really bring that much to the table - it relies on
    > inheriting NSView to store its subviews, the only thing it draws is the
    > actual splitter itself, which is  just a line (or narrow bar if you are
    > using that style).

    The other thing NSSplitView does is interface with the live-resize
    machinery; subviews of an NSSplitView will return YES from -inLiveResize
    while a splitter is being dragged.

    --Kyle Sluder
  • Thanks again Graham and all the rest --- We're going somewhere now.

    On 3 ביול 2012, at 08:22, Graham Cox wrote:

    >
    > On 03/07/2012, at 2:46 PM, Motti Shneor wrote:
    >
    >> have the feeling I "almost got it", and I even think I understand why and when delegate methods are being called.  Rolling out my own SplitView doesn't seem to be easier than finding the answer to my question, because to inherit from NSSplitView I'd still need to understand how "super" works won't I?.
    >> Also I'll have to re-implement lots of behavior (animation, dynamics, efficient drawing etc.) that might prove a big job.
    >
    >
    > True in part. But you could subclass NSView instead. After all, NSSplitView doesn't really bring that much to the table - it relies on inheriting NSView to store its subviews, the only thing it draws is the actual splitter itself, which is  just a line (or narrow bar if you are using that style). In fact NSSplitView doesn't animate - if you want to make it do so, you have to add all the animation yourself (for example a panel sliding in and out of view). By the time you've got it bent to your will, you'll have written so much code in the delegate that you may as well have written a custom view class in the first place. It doesn't even give you a particularly easy way of setting the split position programatically.

    NSSplitView also implements

    1. Live resizing, with extra logic (when to collapse etc.)
    2. Many small UI intricacies (e.g. when dividers fall off)
    3. It implements Apple's standard visual appearance (which I want preserved, but also compatible with future changes).
    4. It DOES animate (sometimes... e.g. when the NSSplitView is resized, its internal sub-views adjustment are animated. To experience that, just click the zoom button of any window of a sample program with a split-view).
    5. It handles mouse and key events! (clickable areas, dragging,  overlapping divider areas etc. For me, event-handling always seems to be the hardest thing to do. I don't want to do synchronous event-loops.)
    6. It handles generic drawing of dividers.
    7. It supports accessibility.

    Isn't this a big job to re-implement? and how would I be "compatible with the future" ?

    >
    >> I could re-state my question again even simpler --- I want to know what makes NSSplitView respond with "YES" to the following:
    >>
    >> [mySplitView isSubviewCollapsed:panelSubview];
    >
    > I think it's that the subview is hidden.

    That's a BIG answer, if it is true. Can we get anyone from Apple to confirm that? In my current code I RELY on this assumption, but it has no confirmation in the docs.

    >
    >> Is it just the subview being hidden? being zero-framed? being vertically/horizontally transformed to zero? Does the NSSplitView maintain a "collapsed" state member for each of its subviews? How can I set this state?
    >>
    >> and if anyone from Apple is on this list --- for god sake, why isn't there a [mySplitView setSubview:panelSubview collapsedStateTo:YES/NO] ????
    >
    > The subview isn't zero framed. Once upon a time it was, but that makes it an even bigger pain than usual to get it back into a sensible state if it contains further views that are autoresized. At some point, it was changed so that the view is maintained at its (minimum?) size and simply hidden. That's my current understanding anyway.

    NSSplitView header docs say that frame hight/width might be zeroed upon collapsing, but that Bounds should remain as they are. Maybe the docs are outdated? how can we confirm that now NSSplitView works with "isHidden" instead?

    >
    > Seems to me a sensible design for a split view would have simple properties like a min and max width for each subview and a sizing priority, and simple methods to show and hide each pane with or without animation. Maybe the newer constraints stuff helps but given that NSSplitView as it stands has to be backward-compatible with all the gnarly code that has gone before to make it work, it's always going to be awkward. It's surprising Apple persist with this class - they should implement a complete replacement which is not a subclass and then new code can adopt it without breaking old code.
    >

    Yay. I'd sure LIKE this kind of split-view! However, I think Apple already has it all in the new layout mechanism. Pity I can't use it in this project.

    > Just my 2¢ worth.

    Oh no... It's worth much more!

    --Motti
  • I always use RBSplitView - has all that stuff built in and more…

    http://brockerhoff.net/blog/tag/rbsplitview/

    Seems to work fine for my needs.

    Regards

    Gideon

    On 03/07/2012, at 2:40 PM, Motti Shneor <sumac...> wrote:

    > Thanks Graham (Sigh…)
    >
    > I was beginning to think I'm stupid or something, struggling so hard with a UI element as ordinary as a Split-View.
    >
    >
    > On 3 ביול 2012, at 03:14, Graham Cox wrote:
    >
    >>
    >> On 03/07/2012, at 12:21 AM, Motti Shneor wrote:
    >>
    >>> I really need an advice here.
    >>
    >>
    >> This will sound flippant but it's not meant to be: implement your own split view.
    >>
  • Hello Gideon.

    Thanks for the note. Indeed, RBSplitView is a complete, functional  and feature-loaded class, but it is also lagging behind current Cocoa developments, and does not integrate well XCode 4.x and with SDK 10.7 or and SDK 10.8. Testing with it I saw problems in:
    1. Look and feel (not coherent with recent standard NSSplitView variants).
    2. Integration with dev. environment (Apple broke the plugin architecture of  Interface-Builder in XCode 4). Everything must be done programmatically.
    3. Does not mix well with auto-layout views. We don't yet use auto-layout (or we wouldn't have so much trouble with the split-view - it does very well with auto-layout policies.)

    On 18 ביול 2012, at 09:05, Gideon King wrote:

    > I always use RBSplitView - has all that stuff built in and more…
    >
    > http://brockerhoff.net/blog/tag/rbsplitview/
    >
    > Seems to work fine for my needs.
    > Regards
    > Gideon
    >

    Motti Shneor
previous month july 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today