C# UIScrollView混合布局方法的问题

C# UIScrollView混合布局方法的问题,c#,ios,xamarin.ios,ios-autolayout,C#,Ios,Xamarin.ios,Ios Autolayout,我正试着跟着你走 我见过其他类似的问题 我不确定这是否是我需要的 请理解,我既不使用故事板也不使用XIB。 我的所有视图都是使用Xamarin.iOS中的c#以编程方式创建的。这与目标C代码非常相似 像许多其他人一样,我找不到一种方法让我的滚动视图真正滚动 因此,在我的主视图控制器中,ViewDidLoad()中有以下内容: 没有什么特别的,我创建了我的UIScrollView,并将其添加为主视图的子视图。创建UIRefresh控件并将其添加到滚动视图。创建一个普通UIView并将其添加到UIS

我正试着跟着你走

我见过其他类似的问题

我不确定这是否是我需要的

请理解,我既不使用故事板也不使用XIB。 我的所有视图都是使用Xamarin.iOS中的c#以编程方式创建的。这与目标C代码非常相似

像许多其他人一样,我找不到一种方法让我的滚动视图真正滚动

因此,在我的主视图控制器中,ViewDidLoad()中有以下内容:

没有什么特别的,我创建了我的UIScrollView,并将其添加为主视图的子视图。创建UIRefresh控件并将其添加到滚动视图。创建一个普通UIView并将其添加到UIScrollView中,以便放置所有我的子视图

因为我使用的是AutoLayout,所以ScrollView被锚定在主视图的约束范围内

现在,在ViewWillAppease()中,我将创建添加到普通UIView中的卡片视图。这些卡片视图使用自动布局相互堆叠,第一张卡片被锚定到容器视图的顶部、前导和尾随锚定

这些卡片中的所有内容都使用自动布局,基本上可以堆叠所有内容

        public UIView BuildQOLCard()
        {
            CardView qolCard = new CardView();
            _scrollViewContainer.AddSubview(qolCard);
            qolCard.TranslatesAutoresizingMaskIntoConstraints = false;
            qolCard.CornerRadius = 5f;
            qolCard.ShadowOffsetHeight = 0;
            qolCard.ShadowOffsetWidth = 0;
            qolCard.BackgroundColor = UIColor.White;
...
           qolCard.Anchor(leading: _scrollViewContainer.LeadingAnchor, trailing: _scrollViewContainer.TrailingAnchor, top: _scrollViewContainer.TopAnchor, padding: new UIEdgeInsets(10f, 10f, 10f, 10f));

            return qolCard;
        }
当然,容器视图不会神奇地知道如何调整自身大小。所以我必须给它一个尺寸,否则我什么也看不见

所以我像这样制作了一个助手:

public void SizeScrollViewContentSize()
{
    nfloat scrollViewHeight = 0.0f;
    foreach (UIView view in this._scrollViewContainer.Subviews)
    {
        scrollViewHeight += view.Frame.Size.Height;
    }

    this._scrollViewContainer.Frame = new CGRect(0, 0, this._scrollView.Frame.Width, scrollViewHeight);
}
但我似乎不知道何时调用它,因为子视图在这里,但它们的大小未知。我尝试从ViewDidLayoutSubviews()调用它

谢谢你的帮助


编辑:对接受的答案应用te建议,导致启用滚动,但滚动视图始终返回顶部

下面是当前的代码:

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

    _scrollView = new UIScrollView
    {
        ShowsHorizontalScrollIndicator = false,
        TranslatesAutoresizingMaskIntoConstraints = false
    };

    if (!UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
    {
        _scrollView.AlwaysBounceVertical = true;
        _scrollView.Bounces = true;
    }

    _refreshControl = new UIRefreshControl { TranslatesAutoresizingMaskIntoConstraints = false };
    _refreshControl.Enabled = true;
    _refreshControl.ValueChanged -= RefreshControl_ValueChanged;
    _refreshControl.ValueChanged += RefreshControl_ValueChanged;
    if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
    {
        _scrollView.RefreshControl = _refreshControl;
    }
    else
    {
        _scrollView.AddSubview(_refreshControl);
    }

    this.View.AddSubview(_scrollView);

    _scrollViewContainer = new UIView { TranslatesAutoresizingMaskIntoConstraints = false };
    _scrollView.AddSubview(_scrollViewContainer);

    _scrollView.Anchor(top: this.View.SaferAreaLayoutGuide().TopAnchor, leading: this.View.LeadingAnchor, bottom: this.View.SaferAreaLayoutGuide().BottomAnchor, trailing: this.View.TrailingAnchor);

    // Only define the width of the container
    _scrollViewContainer.TopAnchor.ConstraintEqualTo(_scrollView.TopAnchor).Active = true;
    _scrollViewContainer.LeadingAnchor.ConstraintEqualTo(_scrollView.LeadingAnchor).Active = true;
    _scrollViewContainer.WidthAnchor.ConstraintEqualTo(_scrollView.WidthAnchor).Active = true;
}
第一张牌:

            CardView qolCard = new CardView();
            _scrollViewContainer.AddSubview(qolCard);
            qolCard.TranslatesAutoresizingMaskIntoConstraints = false;
            qolCard.CornerRadius = 5f;
            qolCard.ShadowOffsetHeight = 0;
            qolCard.ShadowOffsetWidth = 0;
            qolCard.BackgroundColor = UIColor.White;
...
           qolCard.Anchor(leading: _scrollViewContainer.LeadingAnchor, trailing: _scrollViewContainer.TrailingAnchor, top: _scrollViewContainer.TopAnchor, padding: new UIEdgeInsets(10f, 10f, 10f, 10f));
卡片2:

               CardView goalsCard = new CardView();
                _scrollViewContainer.AddSubview(goalsCard);
                goalsCard.TranslatesAutoresizingMaskIntoConstraints = false;
                goalsCard.CornerRadius = 5f;
                goalsCard.ShadowOffsetHeight = 0;
                goalsCard.ShadowOffsetWidth = 0;
                goalsCard.BackgroundColor = UIColor.White;
...
               // Top should be constrained to the previous CardView's Bottom
                goalsCard.Anchor(leading: _scrollViewContainer.LeadingAnchor, trailing: _scrollViewContainer.TrailingAnchor, top: qolCard.BottomAnchor,padding: new UIEdgeInsets(10f, 10f, 10f, 10f));
最终锚定:

           // constraint the last card bottom to the bottom of the scrollview container
            goalsCard.Anchor(bottom: context._scrollViewContainer.BottomAnchor);
锚辅助对象:

   internal static void Anchor(this UIView uIView, NSLayoutYAxisAnchor top = null, NSLayoutXAxisAnchor leading = null, NSLayoutYAxisAnchor bottom = null, NSLayoutXAxisAnchor trailing = null, UIEdgeInsets padding = default, CGSize size = default)
    {
        uIView.TranslatesAutoresizingMaskIntoConstraints = false;
        if (top != null)
        {
            uIView.TopAnchor.ConstraintEqualTo(top, padding.Top).Active = true;
        }

        if (leading != null)
        {
            uIView.LeadingAnchor.ConstraintEqualTo(leading, padding.Left).Active = true;
        }

        if (bottom != null)
        {
            uIView.BottomAnchor.ConstraintEqualTo(bottom, -padding.Bottom).Active = true;
        }

        if (trailing != null)
        {
            uIView.TrailingAnchor.ConstraintEqualTo(trailing, -padding.Right).Active = true;
        }

        if (size.Width != 0)
        {
            uIView.WidthAnchor.ConstraintEqualTo(size.Width).Active = true;
        }

        if (size.Height != 0)
        {
            uIView.HeightAnchor.ConstraintEqualTo(size.Height).Active = true;
        }
    }

滚动视图需要定义其框架需要定义其内容。如果内容约束正确,则滚动是自动的

我将一步一步地使用故事板,这样我们就可以看到发生了什么。当我们通过代码来实现它时,绝对没有什么不同

从视图控制器开始,添加一个滚动视图,并将其约束到所有四个面(我给了它一个绿色背景,以便我们可以看到它):

现在,我们添加一个“容器”视图(青色背景),并将所有4个边约束到滚动视图:

如果运行此操作,我们会看到:

这是因为“容器”没有宽度或高度。它的前导/尾随/顶部/底部约束定义了滚动视图的内容

因此,让我们将其约束为等宽度和等高度到滚动视图:

运行它,我们可以看到:

这很好,除了。。。它永远不会滚动,因为它与滚动视图的高度相同

因此,我删除了等高,因为我希望容器的内容确定其高度

我添加了一个“cardwiew”,并将其顶部/前导/尾随约束设置为“container”的顶部/前导/尾随,其中10点为“padding”,高度为100。我还为“container”(容器)的底部(+10)提供了一个底部约束,该约束将“拉动容器底部”,使其在CardView底部下方10点处:

导致:

现在,我添加了第二个CardView,将前导/尾随约束约束约束到容器的前导/尾随,具有10点“padding”,高度为100。我将其顶部约束设置为CardView-1(+10)的底部,并将其底部约束为“容器”(+10)的底部:

不幸的是,我们现在有一个来自CardView-1底部的10 pt约束和一个来自CardView-2底部的10 pt约束-当然,这不会起作用

因此,让我们从CardView-1中删除“从底部到超级视图”的约束

我们得到这个:

让我们为更多的卡片视图重复这些步骤,每次将前导/尾随约束为“容器”,高度为100,从上一张卡片视图的顶部到底部。。。然后仅将最后一个卡片视图的底部约束到“容器”的底部:

耶!我们可以滚动:

通过代码执行此操作只需记住,每个CardView的顶部都应约束到上一个
CardView的底部——除了第一个
卡,您将其约束到“容器”的顶部和最后一个
卡,您将其约束到“容器”的底部

所有这些都是通过自动布局完成的。无需执行任何“计算高度和设置框架”代码


作为补充说明,您可以使用StackView并消除其中的大部分:

  • 忘记“容器”
  • 将堆栈视图添加到滚动视图
    • 轴:垂直,间距:10
  • 使用10 pts填充将堆栈视图的所有4个边约束到滚动视图
  • 为堆栈视图提供一个宽度约束,该约束等于滚动视图宽度-20(对于每侧的10点填充)
然后,只需将CardView添加为堆栈视图的排列子视图


还有。。。。完成了

谢谢你的解释。只是一个快速更新,让你知道它滚动,但它总是反弹回顶部。有什么问题吗?@OlivierMATROT-您必须向我展示您正在使用的代码,以便找出原因。@OlivierMATROT-可能有一些问题。。。您是否每次拉刷新时都会添加一张新的“卡”?如果是这样,当您向新添加的卡添加底部锚定时,您是否正在移除上一张卡的底部锚定?另外,当刷新控件结束刷新时,它会将内容滚动回顶部。。。我不使用Xamarin/C#,所以我不能给你一个完整的例子(我可以给你一个Swift或Objective-C)当我拉刷新时,我从scrollViewContainer中删除所有子视图(卡片),并用更新的数据将它们添加回来。问题是我甚至不能滚动到机器人
   internal static void Anchor(this UIView uIView, NSLayoutYAxisAnchor top = null, NSLayoutXAxisAnchor leading = null, NSLayoutYAxisAnchor bottom = null, NSLayoutXAxisAnchor trailing = null, UIEdgeInsets padding = default, CGSize size = default)
    {
        uIView.TranslatesAutoresizingMaskIntoConstraints = false;
        if (top != null)
        {
            uIView.TopAnchor.ConstraintEqualTo(top, padding.Top).Active = true;
        }

        if (leading != null)
        {
            uIView.LeadingAnchor.ConstraintEqualTo(leading, padding.Left).Active = true;
        }

        if (bottom != null)
        {
            uIView.BottomAnchor.ConstraintEqualTo(bottom, -padding.Bottom).Active = true;
        }

        if (trailing != null)
        {
            uIView.TrailingAnchor.ConstraintEqualTo(trailing, -padding.Right).Active = true;
        }

        if (size.Width != 0)
        {
            uIView.WidthAnchor.ConstraintEqualTo(size.Width).Active = true;
        }

        if (size.Height != 0)
        {
            uIView.HeightAnchor.ConstraintEqualTo(size.Height).Active = true;
        }
    }