我需要做这个有奇怪配置的应用程序。
如下图所示,主视图是UIScrollView。然后在里面它应该有一个UIPageView,并且PageView的每个页面都应该有一个UITableView。
到目前为止,我已完成了所有这些工作。但我的问题是我希望滚动到表现 自然。
接下来是我的意思自然。目前,当我滚动其中一个UITableViews时,它会滚动tableview(而不是scrollview)。但我希望它滚动ScrollView,除非滚动视图无法滚动导致它到达顶部或底部(在这种情况下我喜欢滚动桌面视图)。
例如,让我们说我的滚动视图目前滚动到顶部。然后我将手指放在桌面视图上(显示当前页面)并开始滚动向下。在这种情况下,我希望滚动视图滚动(没有tableview)。如果我继续向下滚动我的滚动视图并且它到达底部,如果我从显示器上移开我的手指并将其放回tebleview并再次向下滚动,我希望我的tableview现在向下滚动因为滚动视图到达底部并且它&# 39;无法继续滚动。
你们有没有想过如何实现这种滚动?
我真的迷失了。任何帮助都会非常感激:(
谢谢!
答案 0 :(得分:47)
同时处理滚动视图和表格视图的解决方案围绕UIScrollViewDelegate
。因此,让您的视图控制器符合该协议:
class ViewController: UIViewController, UIScrollViewDelegate {
我将滚动视图和表视图表示为outlet:
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!
我们还需要跟踪滚动视图内容的高度以及屏幕高度。你以后会明白为什么。
let screenHeight = UIScreen.mainScreen().bounds.height
let scrollViewContentHeight = 1200 as CGFloat
viewDidLoad:
需要一点配置:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight)
scrollView.delegate = self
tableView.delegate = self
scrollView.bounces = false
tableView.bounces = false
tableView.scrollEnabled = false
}
我已经关掉弹跳以保持简单。 关键设置是滚动视图和表格视图的委托,并且首先关闭表格视图滚动。
这些是必要的,以便scrollViewDidScroll:
委托方法可以处理到达滚动视图的底部并到达表视图的顶部。这是方法:
func scrollViewDidScroll(scrollView: UIScrollView) {
let yOffset = scrollView.contentOffset.y
if scrollView == self.scrollView {
if yOffset >= scrollViewContentHeight - screenHeight {
scrollView.scrollEnabled = false
tableView.scrollEnabled = true
}
}
if scrollView == self.tableView {
if yOffset <= 0 {
self.scrollView.scrollEnabled = true
self.tableView.scrollEnabled = false
}
}
}
委托方法正在做的是检测滚动视图何时到达底部。发生这种情况时,可以滚动表视图。它还检测表视图何时到达重新启用滚动视图的顶部。
我创建了一个GIF来演示结果:
答案 1 :(得分:17)
修改Daniel的答案,使其更有效率和无bug。
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableHeight: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
//Set table height to cover entire view
//if navigation bar is not translucent, reduce navigation bar height from view height
tableHeight.constant = self.view.frame.height-64
self.tableView.isScrollEnabled = false
//no need to write following if checked in storyboard
self.scrollView.bounces = false
self.tableView.bounces = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30))
label.text = "Section 1"
label.textAlignment = .center
label.backgroundColor = .yellow
return label
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row: \(indexPath.row+1)"
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.scrollView {
tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200)
}
if scrollView == self.tableView {
self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
}
}
完整的项目可以在这里看到: https://gitlab.com/vineetks/TableScroll.git
答案 2 :(得分:4)
经过多次试验和错误后,这对我来说最有效。解决方案必须解决两个需求1)确定应该使用谁的滚动属性; tableView还是scrollView? 2)确保tableView没有授予scrollView权限,直到它到达其表格/内容的顶部。
为了查看滚动视图是否应该用于滚动与tableview,我检查了我的tableview正上方的UIView是否在帧内。如果UIView在帧内,可以肯定地说scrollView应该有权滚动。如果UIView不在框架内,这意味着tableView占用了整个窗口,因此应该有权滚动。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.intersects(UIView.frame) == true {
//the UIView is within frame, use the UIScrollView's scrolling.
if tableView.contentOffset.y == 0 {
//tableViews content is at the top of the tableView.
tableView.isUserInteractionEnabled = false
tableView.resignFirstResponder()
print("using scrollView scroll")
} else {
//UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView.
tableView.isUserInteractionEnabled = true
scrollView.resignFirstResponder()
print("using tableView scroll")
}
} else {
//UIView is not in frame. Use tableViews scroll.
tableView.isUserInteractionEnabled = true
scrollView.resignFirstResponder()
print("using tableView scroll")
}
}
希望这有助于某人!
答案 3 :(得分:1)
我认为有两种选择。
由于您知道滚动视图和主视图的大小,因此您无法判断滚动视图是否触及底部。
if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
// reach bottom
}
所以当它击中时;你基本上设置了
[contentScrollView setScrollEnabled:NO];
和你的tableView的其他方式。
另一件事,我认为更精确的是将Gesture添加到您的观点中。
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToTapGesture:)];
// Specify that the gesture must be a single tap
tapRecognizer.numberOfTapsRequired = 1;
// Add the tap gesture recognizer to the view
[self.view addGestureRecognizer:tapRecognizer];
// Do any additional setup after loading the view, typically from a nib
因此,当您添加手势时,您只需更改setScrollEnabled
中的respondToTapGesture
即可控制有效视图。
答案 4 :(得分:1)
这里的答案都不适合我。每个人都有自己的细微问题(当一个滚动视图击中它的底部时需要重复滑动,或滚动指示器看起来不正确等等),所以我想再投入一个答案。
Ole Begemann非常清楚地写了https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/
尽管这是一个旧帖子,但这些概念仍然适用于当前的API。此外,他的方法https://github.com/eyeem/OLEContainerScrollView
还保持了(Xcode 9兼容)Objective-C实现答案 5 :(得分:1)
我也在努力解决这个问题。有一个非常简单的解决方案。
在界面构建器中:
现在,这是有趣的部分:对于您添加的每个页面,请确保其tableView的顶部偏移。而已。例如,您可以使用以下代码进行操作(假设您使用UITableViewController作为页面):
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let tables = viewControllers.compactMap { $0 as? UITableViewController }
tables.forEach {
$0.tableView.contentInset = UIEdgeInsets(top: headerView.bounds.height, left: 0, bottom: 0, right: 0)
$0.tableView.contentOffset = CGPoint(x: 0, y: -headerView.bounds.height)
}
}
在表格视图内的滚动中没有混乱的滚动,没有与委托的混乱,没有重复的滚动,完全自然的行为。如果看不到标题,则可能是因为tableView背景色。您必须将其设置为clear,以便表头在tableView下可见。
答案 6 :(得分:0)
我发现了一个很棒的库 MXParallaxHeader
在故事板中,只需将UIScrollView
课程设为MXScrollView
,就会发生魔法。
当我嵌入UIScrollView
容器视图时,我使用此类来处理UIPageViewController
。即使您可以插入视差标题视图以获取更多细节。
此外,此库提供Cocoapods
和Carthage
我附上了一张代表UIView
层次结构的图片。
MXScrollView Hierarchy
答案 7 :(得分:0)
也许是蛮力的,但是运行得很好:顺便说一下,我使用自动布局。
对于tableView(或collectionView或其他),在情节提要中设置任意高度,并为类提供一个出口。 (viewDidLoad()或...)在适当的地方将tableView的高度设置得足够大,以使tableView不需要滚动。 (需要提前知道行数),然后只有外部scrollView会很好地滚动。
答案 8 :(得分:0)
如果您面临嵌套滚动问题,请使用以下最简单的解决方案。
答案 9 :(得分:0)
一个简单的技巧,如果要实现它,那就是用普通的容器视图替换父滚动视图。 在容器视图上添加平移手势,您可以在第一个视图的顶部约束下玩以分配负值。您可以检查页面视图的来源,如果它达到顶部,则可以开始在页面视图的子视图的内容偏移量上分配该值。直到用户在容器视图中以最高级视图的状态获得表格视图之前,您可以禁用页面tableView的滚动,并可以通过设置内容偏移来手动滚动。 因此,最初的页面浏览高度将在底部折叠(或说在屏幕外)或更少。稍后向下滚动时,它将展开以占用更多空间。
如果导航栏或容器视图之外的其他视图中显示的帧数过多,Gesture将自动停止响应。
手势是许多应用中使用的用户交互过渡的关键。您可以使用它来模拟滚动一定时间。
答案 10 :(得分:0)
在我的情况下,我对高度使用约束:
self.heightTableViewConstraint.constant = self.tableView.contentSize.height
self.scrollView.contentInset.bottom = self.tableView.contentSize.height
答案 11 :(得分:0)
SWIFT 5
当由于各种不同的屏幕尺寸而无法保证scrollView内容偏移量(Y)时,我在使用Vineet的答案时遇到了一些麻烦。为了解决这个问题,我更改了启用tableView滚动时的第一个触发事件。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.contains(button.frame) {
tableView.isScrollEnabled = true
}
if scrollView == tableView {
self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
}
}
scrollView.bounds.contains将检查给定元素的框架是否在scrollView的可见内容内为FULLY。我将其设置为tableView下面的按钮。如果您唯一的条件是tableView完全可见,则可以将其设置为tableVIew的框架。
我保留了何时禁用tableView滚动的原始实现,并且效果很好。
答案 12 :(得分:-1)
CGFloat tableHeight = 0.0f;
YourArray =[response valueForKey:@"result"];
tableHeight = 0.0f;
for (int i = 0; i < [YourArray count]; i ++) {
tableHeight += [self tableView:self.aTableviewDoc heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}
self.aTableviewDoc.frame = CGRectMake(self.aTableviewDoc.frame.origin.x, self.aTableviewDoc.frame.origin.y, self.aTableviewDoc.frame.size.width, tableHeight);