Swift 中 NSLayoutConstraints 的奇怪行为

Posted

技术标签:

【中文标题】Swift 中 NSLayoutConstraints 的奇怪行为【英文标题】:Strange behaviour of NSLayoutConstraints in Swift 【发布时间】:2018-09-26 13:18:41 【问题描述】:

我的视图顶部有一个 textField,而 tableView 应该放在 textField 下方。

所以我添加它们的代码如下:

private func setupSearchBar() 
    searchtextField = UITextField()
    mapView.addSubview(searchtextField)
    searchtextField.translatesAutoresizingMaskIntoConstraints = false

    let constraints = [
        NSLayoutConstraint(item: searchtextField, attribute: .leading, relatedBy: .equal, toItem: mapView.safeAreaLayoutGuide, attribute: .leading, multiplier: 1.0, constant: 16.0),
        NSLayoutConstraint(item: searchtextField, attribute: .trailing, relatedBy: .equal, toItem: mapView.safeAreaLayoutGuide, attribute: .trailing, multiplier: 1.0, constant: -16.0),
        NSLayoutConstraint(item: searchtextField, attribute: .top, relatedBy: .equal, toItem: mapView.safeAreaLayoutGuide, attribute: .top, multiplier: 1.0, constant: 24.0),
        NSLayoutConstraint(item: searchtextField, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 46.0)
        ]

    NSLayoutConstraint.activate(constraints)

在此之后我正在设置我的 tableView:

private func setupResultsTableView() 
    tableView = UITableView()
    mapView.addSubview(tableView)
    tableView.translatesAutoresizingMaskIntoConstraints = false

    tableViewHeightAnchor = tableView.heightAnchor.constraint(equalToConstant: 0)
    let constraints = [
        tableView.safeAreaLayoutGuide.topAnchor.constraint(equalTo: searchtextField.safeAreaLayoutGuide.bottomAnchor),
        tableView.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: mapView.safeAreaLayoutGuide.leadingAnchor),
        tableView.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: mapView.trailingAnchor),
        tableViewHeightAnchor!,
        tableView.safeAreaLayoutGuide.bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: view.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0)
    ]
    NSLayoutConstraint.activate(constraints)

我想做的是: 1.设置tableView高度为0 2.当用户开始输入我的textField时,根据搜索结果显示tablView并将其高度设置为tableView的contentSize。所以最大尺寸会贴在设备底部,如果有1-2个结果显示,那么就让tableView变小。

当用户开始输入时,我运行下一个:

func textFieldDidBeginEditing(_ textField: UITextField)         
    filterContentForSearchText(textField.text!)
    tableView.reloadData()

    UIView.animate(withDuration: 0.6) 
        self.tableView.layoutIfNeeded()

        if textField.isEditing 
            self.tableViewHeightAnchor.constant = self.tableView.contentSize.height
         else 
            self.tableViewHeightAnchor.constant = 0.0
        
    

但是我的代码有问题,因为我得到了如此丑陋的结果: 当我开始向上滑动时,tableView 开始向上并越过我的 textField

当我完成滚动时,它会停在视图中间

有没有办法解决这个问题?有什么问题可以问我

【问题讨论】:

"将其高度设置为 contentSize" 这个计算似乎是错误的。这就是为什么您的表格视图单元格在视图中间结束的原因。您不应该设置 tableView IMO 的 contentsize。它自己照顾它。 @RakeshaShastri 但我想动态调整 tableView 的高度 设置tableview的contentsize会如何减小tableview的尺寸? @RakeshaShastri 啊不,我将 tableView 的高度设置为其 contentSize。让我更新我的代码 我在任何地方都看不到该代码。 【参考方案1】:

让我们设置 tableView 全高并切换其可见性,如下所示:

tableView.backgroundColor = .clear
tableView.isHidden = true
tableView.tableFooterView = UIView()

对于 tableView 有这些约束:

NSLayoutConstraint.activate([
    tableView.topAnchor.constraint(equalTo: searchtextField.bottomAnchor),
    tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
    tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
    tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0)
])

现在我们可以使用UITextFieldDelegate 方法来切换我们的tableView:

func textFieldDidBeginEditing(_ textField: UITextField) 
    self.toggleSearch(true)


func textFieldShouldReturn(_ textField: UITextField) -> Bool 
    textField.resignFirstResponder()
    return true


func textFieldDidEndEditing(_ textField: UITextField) 
    self.toggleSearch(false)


private func toggleSearch(_ value: Bool) 
    self.tableView.isHidden = !value

别忘了设置searchtextField.delegate = self

另外,您可以添加一个半透明的 backgroundView 并使用这个动画版本的toggleSearch 来淡入/淡出 tableView:

let backgroundView = UIView()
backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.3)
backgroundView.layer.opacity = 0
tableView.backgroundView = backgroundView

private func toggleSearch(_ value: Bool) 
    var completionBlock: ((Bool) -> Void)?
    if (value) 
        self.tableView.isHidden = false
     else 
        completionBlock =  [weak self] _ in self?.tableView.isHidden = true 
    

    UIView.animate(withDuration: 0.3, animations: 
        self.tableView.backgroundView?.layer.opacity = value ? 1 : 0
    , completion: completionBlock)

【讨论】:

非常感谢!它对我帮助很大,但我遇到了一些问题,当我的 tableView 只有一行时 - 我无法选择它,但当它们很少时是可能的,例如 2+。你能告诉我问题是什么或可能是什么吗? 无法选择你的意思是tableView(_ tableView: UITableView, didSelectRowAt indexPath没有被调用?可能是覆盖 tableView 顶部的视图,或者带有单元格的东西,尝试在模拟器中运行并查看 Debug View Hierarchy 或此 teamtreehouse.com/community/… 或此 ***.com/a/33226911/1244597 不能确定,如果没有任何帮助,请创建一个要点.swift 文件并分享链接

以上是关于Swift 中 NSLayoutConstraints 的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

页面布局的四种方式

以编程方式向UITextField添加约束

使用故事板中的自动布局使 UIButtons 居中

UIView animateWithDuration 动画块之外的动画自动布局视图

iOS开发之Masonry框架源码解析

iOS开发之Masonry框架源码深度解析