macos – 具有匹配宽度DocumentView的NSScrollView

首先,我想说我已经看了很多其他资源试图实现这一点,但我看到的任何东西似乎都没有帮助.

例如:Automatically grow document view of NSScrollView using auto layout?

我有一个我在Interface Builder中创建的NSScrollView并设置约束以填充它所在的窗口:滚动视图的顶部,左侧,右侧和底部设置为等于超级视图的顶部,左侧,右侧和底部(其中是Xamarin中viewcontroller使用的xib视图.

我想要的文档视图是一个简单的NSView,它在我的viewcontroller的ViewDidLoad中动态填充了NSImageViews:

EventListScrollView.TranslatesAutoresizingMaskIntoConstraints = false;

var documentView = new NSView(EventListScrollView.Bounds);
//documentView.AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.HeightSizable;
//documentView.TranslatesAutoresizingMaskIntoConstraints = false;

NSView lastHeader = null;
foreach(var project in _projects)
{
    var imageView = new NSImageView();
    imageView.TranslatesAutoresizingMaskIntoConstraints = false;
    imageView.SetContentCompressionResistancePriority(1, NSLayoutConstraintOrientation.Horizontal);
    imageView.SetContentCompressionResistancePriority(1, NSLayoutConstraintOrientation.Vertical);
    imageView.ImageScaling = NSImageScale.ProportionallyUpOrDown;

    var xPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Left, 1, 0);
    NSLayoutConstraint yPosConstraint = null;
    if(lastHeader!=null)
    {
        yPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, lastHeader, NSLayoutAttribute.Bottom, 1, 0);
    }
    else
    {
        yPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Top, 1, 0);
    }

    var widthConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Width, 1, 0);
    var heightConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, imageView, NSLayoutAttribute.Width, (nfloat)0.325, 0);

    documentView.AddSubview(imageView);
    documentView.AddConstraints(new[] { xPosConstraint, yPosConstraint, widthConstraint, heightConstraint });

    lastHeader = imageView;
    ImageService.Instance.LoadUrl($"https:{project.ProjectLogo}").Into(imageView);
}

EventListScrollView.DocumentView = documentView;

因为我不一定知道_projects中有多少项目,我不知道将加载到每个imageView中的图像的高度,虽然我知道预期的比例,但我不知道我的文档的高度直到运行时

我希望每次滚动视图所在的封闭窗口(因此也调整滚动视图)时,documentView的宽度都会调整为与NSClipView相同.至于documentView的高度,我希望它由我在documentView中填充的imageViews的大小决定,这就是高度约束相对于宽度的原因.

我已经使用了documentView的AutoresizingMask,NSClipView,scrollview等.我尝试将TranslatesAutoresizingMaskIntoConstraints设置为false,并添加约束以将documentView的Left,Right和Top设置为等于NSClipView的约束.当我这样做时,视图似乎甚至没有出现.我似乎无法想出这个.

我也试过这样做而不使用NSImageViews:

public override void ViewDidLoad()
{
    base.ViewDidLoad();

    EventListScrollView.TranslatesAutoresizingMaskIntoConstraints = false;
    var clipView = new NSClipView
    {
        TranslatesAutoresizingMaskIntoConstraints = false
    };

    EventListScrollView.ContentView = clipView;
    EventListScrollView.AddConstraint(NSLayoutConstraint.Create(clipView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, EventListScrollView, NSLayoutAttribute.Left, 1, 0));
    EventListScrollView.AddConstraint(NSLayoutConstraint.Create(clipView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, EventListScrollView, NSLayoutAttribute.Right, 1, 0));
    EventListScrollView.AddConstraint(NSLayoutConstraint.Create(clipView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, EventListScrollView, NSLayoutAttribute.Top, 1, 0));
    EventListScrollView.AddConstraint(NSLayoutConstraint.Create(clipView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, EventListScrollView, NSLayoutAttribute.Bottom, 1, 0));

    var documentView = new NSView();
    documentView.WantsLayer = true;
    documentView.Layer.BackgroundColor = NSColor.Black.CGColor;
    documentView.TranslatesAutoresizingMaskIntoConstraints = false;
    EventListScrollView.DocumentView = documentView;
    clipView.AddConstraint(NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, clipView, NSLayoutAttribute.Left, 1, 0));
    clipView.AddConstraint(NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, clipView, NSLayoutAttribute.Right, 1, 0));
    clipView.AddConstraint(NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, clipView, NSLayoutAttribute.Top, 1, 0));

    NSView lastHeader = null;
    foreach(var project in _projects)
    {
        var random = new Random();

        var imageView = new NSView();
        imageView.TranslatesAutoresizingMaskIntoConstraints = false;
        imageView.WantsLayer = true;
        imageView.Layer.BackgroundColor = NSColor.FromRgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)).CGColor;

        var xPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Left, 1, 0);
        NSLayoutConstraint yPosConstraint = null;
        if(lastHeader!=null)
        {
            yPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, lastHeader, NSLayoutAttribute.Bottom, 1, 0);
        }
        else
        {
            yPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Top, 1, 0);
        }

        var widthConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Width, 1, 0);
        var heightConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, imageView, NSLayoutAttribute.Width, (nfloat)0.325, 0);

        documentView.AddSubview(imageView);
        documentView.AddConstraints(new[] { xPosConstraint, yPosConstraint, widthConstraint, heightConstraint });

        lastHeader = imageView;
    }
}

最佳答案 我想到了.所以,这样做的诀窍在于,除非你将底部固定在最后一个子视图中,否则documentView不会知道自己有多大.您可以使用可视化格式执行此操作,但如果您在运行时之前实际上并不知道您的视图是什么,那就有点了.这是适合我的代码:

public override void ViewDidLoad()
{
    base.ViewDidLoad();

    var clipView = EventListScrollView.ContentView;

    var documentView = new FlippedView();
    documentView.TranslatesAutoresizingMaskIntoConstraints = false;
    EventListScrollView.DocumentView = documentView;
    clipView.AddConstraint(NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, clipView, NSLayoutAttribute.Left, 1, 0));
    clipView.AddConstraint(NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, clipView, NSLayoutAttribute.Right, 1, 0));
    clipView.AddConstraint(NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, clipView, NSLayoutAttribute.Top, 1, 0));

    NSView lastHeader = null;
    foreach (var project in _projects)
    {
        var imageView = new NSImageView();
        imageView.TranslatesAutoresizingMaskIntoConstraints = false;
        imageView.SetContentCompressionResistancePriority(1, NSLayoutConstraintOrientation.Horizontal);
        imageView.SetContentCompressionResistancePriority(1, NSLayoutConstraintOrientation.Vertical);
        imageView.ImageScaling = NSImageScale.ProportionallyUpOrDown;

        var xPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Left, 1, 0);
        NSLayoutConstraint yPosConstraint = null;
        if (lastHeader != null)
        {
            yPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, lastHeader, NSLayoutAttribute.Bottom, 1, 0);
        }
        else
        {
            yPosConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Top, 1, 0);
        }

        var widthConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, documentView, NSLayoutAttribute.Width, 1, 0);
        var heightConstraint = NSLayoutConstraint.Create(imageView, NSLayoutAttribute.Height, NSLayoutRelation.LessThanOrEqual, imageView, NSLayoutAttribute.Width, (nfloat)0.360, 0);

        documentView.AddSubview(imageView);
        documentView.AddConstraint(xPosConstraint);
        documentView.AddConstraint(yPosConstraint);
        documentView.AddConstraint(widthConstraint);
        documentView.AddConstraint(heightConstraint);

        lastHeader = imageView;
        ImageService.Instance.LoadUrl($"https:{project.ProjectLogo}").Into(imageView);
    }

    if(lastHeader!=null)
    {
        var bottomPinConstraint = NSLayoutConstraint.Create(documentView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, lastHeader, NSLayoutAttribute.Bottom, 1, 0);
        documentView.AddConstraint(bottomPinConstraint);
    }
}

关键是最后一个约束 – bottomPinConstraint.这允许documentView扩展其高度以适应所有子视图.

另外,我仍然需要使用IsFlipped = true的视图;即使我手动设置NSClipView和我的documentView之间的约束,否则它会忽略约束并将我的视图固定到NSClipView的底部.我的EventListScrollView是在Interface Builder中创建的,并且设置了约束以使其填充整个窗口.

最终结果是一个带有DocumentView的NSScrollView,它填充了ScrollView的宽度,但也垂直扩展以适应我以编程方式添加到它的子视图.

点赞