指定NSLayoutConstraint的UILayoutPriority好像没有生效

Posted

技术标签:

【中文标题】指定NSLayoutConstraint的UILayoutPriority好像没有生效【英文标题】:Specifying UILayoutPriority of NSLayoutConstraint doesn't seem to take effect 【发布时间】:2019-07-10 20:48:45 【问题描述】:

我在 UIScrollView 中有一个表格和一个按钮,我想要以下设计:

按钮至少在表格最后一行下方 40 pts 按钮总是在视图末端上方 83 pts

通过以下限制,我设法使按钮始终在表格最后一行下方 40 pts,并且仅当表格足够长时才在视图末尾上方 83 pts。在我看来,bottomConstraint 的优先级没有正确覆盖topConstraint 的约束。我已将滚动视图设置为包含整个屏幕。

/* - Sign Out button is 40 pts tall - */
let heightConstraint = NSLayoutConstraint(item: signOutBtn, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 41)

/* - Sign Out button is ALWAYS 83 pts above bottom of screen, when visible - */
let bottomConstraint = NSLayoutConstraint(item: signOutBtn, attribute: .bottom, relatedBy: .equal, toItem: scrollView, attribute: .bottom, multiplier: 1, constant: -83)
bottomConstraint.priority = UILayoutPriority.required

/* - Sign Out button is AT LEAST 40 pts below last row of table - */
let topConstraint = NSLayoutConstraint(item: signOutBtn, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: tableView, attribute: .bottom, multiplier: 1, constant: 40)
topConstraint.priority = UILayoutPriority.defaultLow

/* - Sign Out button stretches across the screen - */
let leadingConstraint = NSLayoutConstraint(item: signOutBtn, attribute: .leading, relatedBy: .equal, toItem: scrollView, attribute: .leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: signOutBtn, attribute: .trailing, relatedBy: .equal, toItem: scrollView, attribute: .trailing, multiplier: 1, constant: 0)

scrollView.addConstraints([heightConstraint, bottomConstraint, leadingConstraint, trailingConstraint, topConstraint])

截图: (糟糕——这就是我现在所完成的)

(好)

如果表格太长,退出按钮不会出现,用户需要向下滚动到它。

【问题讨论】:

这和你之前的问题一样吗? ***.com/questions/56972736/… @DonMag 我提出了一个新问题,因为这让我感到困惑,为什么我添加的约束优先级似乎没有任何效果,而之前的问题是问我应该采取什么方法来实现这种效果. 这仍然令人困惑...根据您的图像,您希望在表格最后一行下方 40 点处有一个按钮。那么视图底部上方的 83 点是什么?或者,当表格很短时,您是否希望按钮位于视图底部上方 83 分(因此底行和按钮之间的间隙可能为 300 分),直到 tableView 增长到“将其推离屏幕”? @DonMag 我明白你的困惑。我已经更新了这个问题,我希望它能解决问题......第一个屏幕截图是我使用已有的约束看到的。剩下的就是我想看到的了。所以我想要一个总是在视图底部上方 83 pts 的按钮,并且随着 tableView 的增长,按钮被按下,在它和表格的最后一行之间保持 40 pts 的间距。 我在对您之前的问题的评论中发布了一个链接,该链接正是您正在尝试做的事情:“这是一种方法:***.com/a/56134043/6257435 .. . 在该示例中,只需将“页脚视图”(这是一个普通的 UIView)替换为您的按钮。” 【参考方案1】:

我创建了以下视图控制器,应该可以解决您的问题 - 我不得不承认我不知道您为什么在布局中使用 scrollView,但我希望我设法重新创建了您的设置。

为了模拟不同的桌子高度,我使用了tableView.heightAnchor.constraint(equalToConstant: 300)

Anchors 用于创建约束。

import UIKit

class ViewController: UIViewController 
    let scrollView = UIScrollView()
    let tableView = UITableView()
    let signOutBtn = UIButton()

    override func viewDidLoad() 
        super.viewDidLoad()

        view.addSubview(scrollView)
        scrollView.addSubview(tableView)
        scrollView.addSubview(signOutBtn)

        tableView.dataSource = self

        signOutBtn.setTitle("BUTTON", for: .normal)
        signOutBtn.setTitleColor(.blue, for: .normal)

        //ScrollView constraints
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)
        ])

        //TableView constraints
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.topAnchor),
            tableView.bottomAnchor.constraint(lessThanOrEqualTo: signOutBtn.topAnchor, constant: -40),
            tableView.heightAnchor.constraint(equalToConstant: 300),
            tableView.trailingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.trailingAnchor),
            tableView.leadingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.leadingAnchor)
        ])

        //SignOutButton constraints
        signOutBtn.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            signOutBtn.heightAnchor.constraint(equalToConstant: 41),
            signOutBtn.bottomAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.bottomAnchor, constant: -83),
            signOutBtn.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])

    


extension ViewController: UITableViewDataSource 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 30
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = UITableViewCell(style: .default, reuseIdentifier: "")
        cell.textLabel?.text = "\(indexPath.row)"
        return cell
    

我还附加了带有结果的屏幕:

Image with table with elements fitting view

Image with table with elements not fitting view

【讨论】:

谢谢。我觉得你可能误解了这个问题,这是我的错,因为我意识到我没有在问题描述中提供足够的细节。我附上了一张新图片,可以说明为什么我需要滚动视图。您在此处的解决方案会将按钮永久固定在一个位置。【参考方案2】:

回答您关于约束优先级的具体问题...

您对滚动视图如何使用约束感到困惑。

在此图像中,标签被限制在滚动视图的顶部,按钮被限制在距离优先级为 250 的标签的 40 点和距离优先级为 1000 的滚动视图底部的 83 点。

底部约束决定了滚动视图内容的.contentSize 高度 - 或“可滚动区域”:

【讨论】:

以上是关于指定NSLayoutConstraint的UILayoutPriority好像没有生效的主要内容,如果未能解决你的问题,请参考以下文章

iOS自动布局:设置尾随空间等于superview的宽度

Masonry 源码简单解析

Masonry 源码简单解析

Masonry 源码简单解析

Masonry 源码简单解析

如何创建返回 NSLayoutConstraint 的函数