Enabling NSScrollView to scroll its contents using Auto Layout in Interface Builder

后端 未结 6 954
时光说笑
时光说笑 2021-01-02 14:17

I have implemented a custom NSView which contains many NSTextFields and other NSViews. I then embedded that custom view in a scroll vi

相关标签:
6条回答
  • 2021-01-02 14:40

    The NSScrollView needs to have an X, Y, width and height constraint so that it keeps it's frame the same even if you have views in NSClipView that have bigger width/height that the ScrollView.

    I have seen that if you have views with bigger dimensions and you don't have these constraints, the NSScrollView's frame is automatically set to be the same as the NSClipView, which would be a bigger frame and it may exceed the size of the Window's Frame.

    That is why sometimes it might appear that the NSScrollView doesn't scroll, but it is actually big enough that it contains all the views, exceeding the Window's frame, and it does not need to scroll.

    I made it work by doing this :

    1. Set the following constraints for NSScrollView

      • Leading Space to Container
      • Top Space to Container
      • Equal Widths
      • Equal Heights

    As seen below

    1

    1. Do the same for every View in NSClipView

      The Views will also keep their sizes after you change the frame size of the NSScrollView, if you want to do that.

    In my case it worked like charm! I have an ImageView inside the NSClipView and it scrolls after you resize the window as well. Hope it works!

    0 讨论(0)
  • 2021-01-02 14:44

    To improve on Ken's answer, here is what I did:

    • First, I read this wonderful tutorial about how to set up an NSScrollView in Interface Builder (IB) using auto layout.

    • Then I did the following:

    • Created an NSScrollView instance in IB;

    • Created a new subclass of NSClipView and added the following code:

    class ScCalendarClipView: NSClipView {
        open override var isFlipped: Bool {
            return true
        }
    } 
    

    • Created the custom document view and embedded it as a child of the NSScrollView instance in IB.

    • Added the following constraints in IB in the view I embedded in the NSScrollView instance (let's call it here: DocumentView)

    // Right
    First Item: SuperView.Trailing
    Relation: Less Than or Equal
    Second Item: DocumentView.Trailing
    Constant: 0
    Priority: 510
    Multiplier: 1
    
    First Item: SuperView.Trailing
    Relation: Greater Than or Equal
    Second Item: DocumentView.Trailing
    Constant: 0
    Priority: 490
    Multiplier: 1
    
    // Left
    First Item: DocumentView.Leading
    Relation: Equal
    Second Item: SuperView.Leading
    Constant: 0
    Priority: 1000
    Multiplier: 1
    
    // Bottom (negative constants increase the size of the document view)
    First Item: SuperView.Bottom
    Relation: Less Than or Equal
    Second Item: DocumentView.Bottom
    Constant: -600
    Priority: 510
    Multiplier: 1
    
    // Bottom (constant should be equal to the bottom constraint above)
    First Item: SuperView.Bottom
    Relation: Greater Than or Equal
    Second Item: DocumentView.Bottom
    Constant: -600
    Priority: 490
    Multiplier: 1
    
    // Top
    First Item: DocumentView.Top
    Relation: Equal
    Second Item: SuperView.Top
    Constant: 0
    Priority: 1000
    Multiplier: 1
    
    0 讨论(0)
  • 2021-01-02 14:52

    For those who struggle like me to set constraints between the document view and the clip view: Make sure the clip view is set to automatic layout. For mysterious reasons, it is not set to automatic by default (at least with Xcode 11). Cfr screenshot of the size inspector below.

    0 讨论(0)
  • 2021-01-02 14:57

    I'm going to re-post my answer from https://stackoverflow.com/a/49947440/2846508.

    Look at Apple's 2012 developer conference videos about Auto Layout for information about using Auto Layout in code.

    Simply use in Interface Builder or in code the approach I recorded in this video tutorial:

    How to use NSScrollView with Auto Layout

    This is the approach I used in this video:

    1. Window -- set delegate and IBOutlet property

    2. ScrollView -- fixed edges, no border, don't draw background

    3. documentView -- fixed edges 0, then another trailing and bottom, clipView ≥ 0 @499 and clipView ≤ 0 @501 for both trailing and bottom constraints to documentView

    4. label and text field in horizontal stack view, in vertical stack view

    5. vertical stack view fixed edges default, then another bottom, bottom ≤ default @499 and ≥ default @750

    6. horizontal stack view leading and trailing fixed 0

    7. label and text field align Y center to horizontal stack view

    8. text field top and bottom and trailing 2 @750, width ≥ 100, height ≥ 22

    9. subsequent horizontal stack views leading and trailing fixed, align text field leadings

    0 讨论(0)
  • 2021-01-02 14:58

    Building on the answer from Ken Thomases, with Swift 4, one can add flipped as a User-Defined attribute to NSClipView, to pin the contents to the top of the scrollview.

    0 讨论(0)
  • 2021-01-02 15:01

    Here's the general approach:

    • Make the document view at least as tall as the clip view. Or, equivalently, make the clip view no taller than the document view.
    • Allow the document view to grow in height, but not shrink beyond what its subviews require.
    • Prevent ambiguity in the document view by having a low priority constraint to make it as small as possible given the other constraints.

    So, for example, there should be a constraint between the clip view's bottom and the document view's bottom, but it should be an inequality: Superview.Bottom <= Document View.Bottom. (Or, equivalently, Document View.Bottom >= Superview.Bottom.)

    Within the document view, you probably have some text field or something at the bottom and a constraint between that and the document view's bottom. Make that constraint an inequality: Superview.Bottom >= Text Field.Bottom + standard spacing.

    That will cause ambiguity as to the height of the document view. It could be any size tall enough to fit all of its subviews. Add a height constraint. Set its priority to 51 and its constant to 0. That is, it wants to make the view have 0 height, but at very low priority so almost anything else will supersede it. But it resolves the ambiguity.

    If you want to allow horizontal scrolling, you need to do the same general thing in the horizontal orientation.


    Update:

    There's another approach. Configure the constraints within the document view to give it a strict size (no inequalities). That's usually a chain of constraints from its top to the top subview, from that subview's bottom to the top of another subview, etc., and from the bottom of the bottom subview to the document view's bottom. Same for leading to trailing.

    Then, the only necessary constraints between the clip view and the document view are top and leading constraints.

    If you test in this configuration, you'll be able to resize and the scroll view will scroll. So, that's good. However, when the scroll view's content area is taller than the document view, the document view will be pinned to the bottom of the content area. You usually want it pinned to the top in that situation.

    The reason is that the clip view is not flipped. Also, it's adjusting its bounds to match the document view. So, even though there's a constraint to keep the document view pinned to the top of the clip view, the top of the clip view isn't where you expect it to be. The clip view puts the document view at (0, 0), which is at the bottom.

    So, the final piece is to create a subclass of NSClipView that overrides -isFlipped to return YES. Then, set the class of the clip view in the NIB to your subclass. After that, it will work as you want.

    0 讨论(0)
提交回复
热议问题