UIScrollView 的右锚点不适用

Posted

技术标签:

【中文标题】UIScrollView 的右锚点不适用【英文标题】:Right anchor of UIScrollView does not apply 【发布时间】:2018-09-22 15:08:10 【问题描述】:

我有一个 UIViewController? SingleEventController 显示事件。因为它要显示动态信息,所以我选择创建一个新类UIScrollViewEventScrollView。我试图意识到,基于这个很好的解释 Answer. 我尝试应用纯自动布局方法:在其中创建另一个UIView contentView,其中包含所有信息并锚定到滚动视图的所有四个锚点。

scrollView的contentSize在SingleEventController中的viewDidLayoutSubviews函数中确定。

我的挣扎是,右锚似乎有问题。所有的信息元素都超出了视图的边界,较大的UILabels 的自动换行不起作用,并且在右侧锚点上布置的每个项目都消失了。 (例如 UITableView nopePeopleTV 中的用户名,它是 contentView 的子视图。)

这看起来像这样:


SingleEventController 中的代码:

view.addSubview(scrollView)
scrollView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: buttonViewDividerView.topAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)

override func viewDidLayoutSubviews() 
    let heightOfAllObjects = scrollView.calculateHeightOfAllObjects()
    scrollView.contentSize = CGSize(width: self.view.frame.width, height: heightOfAllObjects + scrollView.heightOfAllPaddings)

EventScrollView中的代码:

addSubview(contentView)
contentView.addSubview(titleLabel)
contentView.addSubview(locationLabel)
...
contentView.addSubview(nopePeopleTV)

contentView.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
titleLabel.anchor(top: contentView.topAnchor, left: contentView.leftAnchor, bottom: nil, right: contentView.rightAnchor, paddingTop: 10, paddingLeft: padding, paddingBottom: 0, paddingRight: padding, width: 0, height: 0)
locationLabel.anchor(top: titleLabel.bottomAnchor, left: contentView.leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: padding, paddingBottom: 0, paddingRight: 0, width: 200, height: 0)
nopePeopleTV.anchor(top: maybePeopleTV.bottomAnchor, left: contentView.leftAnchor, bottom: nil, right: contentView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)

layoutIfNeeded()
contentView.layoutIfNeeded()

我做错了什么?


我的锚函数如下所示:

func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?,  paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat) 

    translatesAutoresizingMaskIntoConstraints = false

    if let top = top 
        topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
    

    if let left = left 
        leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
    

    if let bottom = bottom 
        bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
    

    if let right = right 
        rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
    

    if width != 0 
        widthAnchor.constraint(equalToConstant: width).isActive = true
    

    if height != 0 
        heightAnchor.constraint(equalToConstant: height).isActive = true
    

【问题讨论】:

如果您停止插入一些显然用于形成约束的第三方库,那么帮助您会容易得多。 如果你想看 5 行代码,那总是一样的...... 总比一行看不懂的代码要好。 可以理解。我现在添加了该功能.. 【参考方案1】:

问题是您让内容视图的宽度由子视图的宽度由内向外决定。您需要为内容视图提供与滚动视图本身的宽度相匹配的宽度约束。

我将通过一个人工但完整的纯代码示例来演示差异:

class ViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()
        let sv = UIScrollView()
        sv.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(sv)
        NSLayoutConstraint.activate([
            sv.topAnchor.constraint(equalTo: self.view.topAnchor),
            sv.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            sv.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            sv.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
        ])
        let cv = UIView() // content view
        cv.translatesAutoresizingMaskIntoConstraints = false
        sv.addSubview(cv)
        NSLayoutConstraint.activate([
            sv.topAnchor.constraint(equalTo: cv.topAnchor),
            sv.leadingAnchor.constraint(equalTo: cv.leadingAnchor),
            sv.trailingAnchor.constraint(equalTo: cv.trailingAnchor),
            sv.bottomAnchor.constraint(equalTo: cv.bottomAnchor),
        ])
        let lab = UILabel()
        lab.translatesAutoresizingMaskIntoConstraints = false
        lab.numberOfLines = 0
        lab.text = Array(repeating: "xxx", count: 100).joined(separator: " ")
        cv.addSubview(lab)
        NSLayoutConstraint.activate([
            lab.topAnchor.constraint(equalTo: cv.topAnchor, constant:30),
            lab.leadingAnchor.constraint(equalTo: cv.leadingAnchor),
            lab.trailingAnchor.constraint(equalTo: cv.trailingAnchor),
        ])
    

结果如下:

我们有一个很长的标签,但它没有换行:它一直向右延伸,离开屏幕。那是因为内容视图本身的右边缘离开了屏幕。那是因为我们没有采取任何措施防止这种情况的发生。长标签本身使内容视图与标签的整个文本一样宽。

现在我将最后一行代码添加到viewDidLoad

    cv.widthAnchor.constraint(
        equalTo:sv.frameLayoutGuide.widthAnchor).isActive = true

结果是这样的:

内容视图现在是滚动视图的宽度,因此标签包裹在滚动视图中。

然而,我们不会对内容视图的高度做同样的事情;我们希望它根据其子视图垂直增长。这样一来,我们就可以垂直滚动(这是您想要的),但不能水平滚动(这也是您想要的)。

【讨论】:

您的“最后一行代码”解决了我的问题!谢谢你。

以上是关于UIScrollView 的右锚点不适用的主要内容,如果未能解决你的问题,请参考以下文章

iOS核心动画:旋转的锚点不正确

JavaFX WebView:使用loadContent()链接到文档中的锚点不起作用

文本锚点不起作用。 (使用canvas.create_window)

防止uiscrollview向右滚动

以编程方式滑动 UIScrollView

防止重叠的 UIScrollView 滚动