我认为你会在尝试切换滚动时打一场失败的战斗。
这是另一种方法...
- 将 tableView 添加为“容器”的子视图
UIView
- 将三个“顶部”视图添加为子视图
- 给tableView一个
contentInset.top三个view的高度加上垂直间距
- 约束三个“顶部”视图,以便
-
3 会粘在顶部,直到被1 推上去
-
2 直到滑到 3 下方,1 将其向上推
- 在 tableView 滚动时更新
1 的顶部约束
您可以使用此示例代码进行尝试(无需@IBOutlet 连接):
class ExampleViewController: UIViewController, UIScrollViewDelegate {
let tableView: UITableView = {
let v = UITableView()
v.translatesAutoresizingMaskIntoConstraints = false
v.separatorInset = .zero
return v
}()
let view1: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.backgroundColor = .systemRed
v.text = "1"
return v
}()
let view2: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.backgroundColor = .systemGreen
v.text = "2"
return v
}()
let view3: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.backgroundColor = .systemBlue
v.text = "3"
return v
}()
// this will hold the tableView and the
// three other views
let containerView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
// clip to bounds to prevent the "top" views from showing
// as they are "pushed up" out of bounds
v.clipsToBounds = true
return v
}()
// this constraint constant will be changed
// in scrollViewDidScroll
@IBOutlet var view1TopConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// add our container view
view.addSubview(containerView)
// add our tableView and three "top" views
containerView.addSubview(tableView)
containerView.addSubview(view2)
containerView.addSubview(view3)
containerView.addSubview(view1)
for v in [view1, view2, view3] {
// all three "top" views should be
// equal width to tableView
// horizontally centered to tableView
// 40-pts tall
NSLayoutConstraint.activate([
v.widthAnchor.constraint(equalTo: tableView.widthAnchor),
v.centerXAnchor.constraint(equalTo: tableView.centerXAnchor),
v.heightAnchor.constraint(equalToConstant: 40.0),
])
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain container view with 20-pts "padding"
containerView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
containerView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
// constrain all 4 sides of tableView ot container view
tableView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0),
tableView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 0.0),
tableView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0.0),
tableView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0),
])
// view3 should stick to the top of the table,
// unless it's being pushed up by view1
let v3Top = view3.topAnchor.constraint(equalTo: tableView.topAnchor)
v3Top.priority = .defaultHigh + 1
// view2 should stick to the bottom of view3,
// unless it's being pushed up by view1
let v2Top = view2.topAnchor.constraint(equalTo: view3.bottomAnchor, constant: 4.0)
v2Top.priority = .defaultHigh
// view1 should ALWAYS be 4-pts from bottom of view2
let v1TopA = view1.topAnchor.constraint(equalTo: view2.bottomAnchor, constant: 4.0)
v1TopA.priority = .required
// view1 should ALWAYS be greater-than-or-equal 4-pts from bottom of view3
let v1TopB = view1.topAnchor.constraint(greaterThanOrEqualTo: view3.bottomAnchor, constant: 4.0)
v1TopB.priority = .required
// view1 top should ALWAYS be greater-than-or-equal top of tableView
let v1TopC = view1.topAnchor.constraint(greaterThanOrEqualTo: tableView.topAnchor)
v1TopC.priority = .required
// 88-pts is 40-pts for view3 and view2 plus 4-pts vertical spacing
// view1 top should NEVER be more-than 88-pts from top of tableView
let v1TopD = view1.topAnchor.constraint(lessThanOrEqualTo: tableView.topAnchor, constant: 88.0)
v1TopD.priority = .required
// view1 top will start at 88-pts from top of tableView
view1TopConstraint = view1.topAnchor.constraint(equalTo: tableView.topAnchor, constant: 88.0)
view1TopConstraint.priority = .defaultHigh + 2
// activate those constraints
NSLayoutConstraint.activate([
v3Top,
v2Top,
v1TopA,
v1TopB,
v1TopC,
v1TopD,
view1TopConstraint,
])
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
// top inset is
// three 40-pt tall views
// plus 4-pts vertical spacing between each
// and 4-pts vertical spacing below view1
tableView.contentInset.top = 132
tableView.contentOffset.y = -132
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// we're getting called when the tableView scrolls
// invert the contentOffset y
let y = -scrollView.contentOffset.y
// subtract 44-pts (40-pt tall view plus 4-pts vertical spacing)
view1TopConstraint.constant = y - 44.0
}
}
extension ExampleViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
c.textLabel?.text = "Row \(indexPath.row)"
return c
}
}
下面是开始的样子:
然后,在向上滚动一点之后(2 在3 下方滑动):
再滚动一点(1 将3 向上推到视野之外):
然后在1 保持在顶部时滚动表格: