双向滚动UITableViews
我已经知道您在想什么……这没有道理。 UITableView是通过垂直滚动来显示其内容的,为什么您要一个双向滚动呢?
当前,在Shine Labs,我们正在构建一个iPad应用程序,该应用程序使用具有非常宽内容的单元格来实现全屏UITableView。 随着开发的进行,客户希望该应用程序同时支持iPad多任务处理和iPhone。 为了做到这一点,我们需要采取一种对时间敏感且微创的方法来适应各种屏幕尺寸。 我们想出的解决方案非常简单,并允许我们重用整个现有的UI,并有一些小的警告。
通过在运行时将UITableView嵌入UIScrollView中,我们可以确定UITableView是否足够宽以容纳其所有内容。 如果没有,我们将其宽度增加一定程度,并使UIScrollView能够水平滚动。
让我们来看一个实际的例子。
在UIViewController的viewDidLoad
方法中,我们配置UITableView和UIScrollView的布局。
为了配置布局,我们使用了自动布局抽象库SnapKit来使约束代码更加简洁。
私人 让 scrollView = UIScrollView()
私人 让 tableView = UITableView()
覆盖 func viewDidLoad(){
超级 .viewDidLoad()
configureConstraints()
}
私人 功能 configureConstraints(){
view.addSubview(scrollView)
scrollView.snp.makeConstraints {
$ 0.edges.equalToSuperview()
}
scrollView.addSubview(tableView)
让 tableViewWidth = view.frame.width
tableView.snp.makeConstraints {
$ 0.edges.equalToSuperview()
$ 0.height.equalTo(视图)
$ 0.width.equalTo(tableViewWidth)
}
}
我们首先将scrollView
边缘固定到视图。 然后,将tableView
边缘固定到scrollView
,使tableView
高度等于view
高度,并为tableView
赋予一个宽度,该宽度的常数等于view.frame.width
。
设置约束后,我们需要配置scrollView
。
覆盖 func viewDidLoad(){
超级 .viewDidLoad()
configureConstraints()
configureScrollView()
}
私人 功能 configureScrollView(){
scrollView.isDirectionalLockEnabled = true
scrollView.bounces = false
}
通过将isDirectionalLockEnabled
设置为true
,我们阻止了scrollView
能够同时在两个方向上滚动。
通过将scrollView
设置为false
,我们可以防止scrollView
过度滚动,并在滚动后执行本机UIScrollView bounce动画。
然后,我们需要确定屏幕尺寸是否足够大以容纳tableView
内容。 为此,我们创建了一个计算属性compactModeActive
。
私人 var compactModeActive:布尔{
// 504是多任务模式下的iPad Pro 10.5“
//根据您的视图需求,此值是任意的
返回 view.frame.width <505
}
最后,在UIViewController的viewDidLayoutSubviews
方法中,我们更新了tableView
宽度,并将scrollView.isScrollEnabled
属性设置为compactModeActive
。
覆盖 func viewDidLayoutSubviews(){
超级 .viewDidLayoutSubviews()
scrollView.isScrollEnabled = compactModeActive
updateConstraints()
}
私人 功能 updateConstraints(){
让 viewWidth = view.frame.width
让 tableViewWidth:CGFloat = compactModeActive吗? viewWidth * 2:viewWidth
tableView.snp.updateConstraints {
$ 0.width.equalTo(tableViewWidth)
}
}
好极了! 我们已经成功实现了tableView
以根据屏幕尺寸水平滚动。 但是我们尚未完成-tableView使用UIRefreshControl作为触发数据重新加载的方式。 由于当compactModeActive
为true
,我们已将tableView的宽度增加了2 compactModeActive
,因此UIRefreshControl仍位于tableView
的中心。
为了正确refreshControl
,我们必须做一点框架数学以更新其bounds
。
私人让 refreshControl = UIRefreshControl()
私人 var refreshControlX:CGFloat {
返回 [[scrollView.contentSize.width / 2)-(scrollView.frame.width / 2))-scrollView.contentOffset.x
}
覆盖 func viewDidLayoutSubviews(){
超级 .viewDidLayoutSubviews()
scrollView.isScrollEnabled = compactModeActive
updateConstraints()
updateRefreshControl()
}
私人 功能 updateRefreshControl(){
守卫 compactModeActive else {
refreshControl.bounds.origin.x = 0
返回
}
refreshControl.bounds.origin.x = refreshControlX
}
私人 功能 configureScrollView(){
scrollView.isDirectionalLockEnabled = true
scrollView.bounces = false
scrollView.delegate =自我
}
扩展 ViewController:UIScrollViewDelegate {
func scrollViewDidScroll( _ scrollView:UIScrollView){
updateRefreshControl()
}
}
首先,我们创建了一个计算属性refreshControlX
,以计算refreshControlX
的值,该值是scrollView.contentSize.width
一半减去scrollView.contentOffset.x
一半减去scrollView.contentOffset.x
。
然后,我们的UIViewController采用了UIScrollViewDelegate,并在scrollViewDidScroll
期间调用了updateRefreshControl
。 当用户来回滚动时,我们将利用scrollView.contentOffset.x
来确定bounds.origin.x
的新refreshControl
。
最后,在viewDidLayoutSubviews
,我们再次调用updateRefreshControl
以根据帧大小的变化重新定位refreshControl
。
就是这样!
感谢您抽出宝贵的时间阅读这篇文章,如果您有任何疑问,请随时发表评论!
示例代码可以在这里找到:https://github.com/shine-labs/Bidirectional-Scrolling-UITableView
如有任何开发疑问,请联系dylan@shinelabs.tech。