我需要做这个有奇怪配置的应用程序。
如下图所示,主视图是UIScrollView。然后里面应该有一个UIPageView,而PageView的每个页面都应该有一个UITableView。
到目前为止,我已经完成了这一切。但我的问题是我希望滚动行为自然。
接下来是我的意思。目前,当我滚动其中一个UITableViews时,它会滚动tableview(而不是scrollview)。但我希望它滚动ScrollView,除非scrollview无法滚动导致它到达顶部或底部(在这种情况下,我希望它滚动tableview)。
例如,假设我的滚动视图当前滚动到顶部。然后我把手指放在桌面视图(显示的当前页面)上并开始向下滚动。在这种情况下,我希望滚动视图滚动(没有tableview)。如果我继续向下滚动我的滚动视图并且它到达底部,如果我从显示器上移开手指并将其放回tebleview并再次向下滚动,我希望我的tableview现在向下滚动,因为滚动视图到达底部并且它不是能够继续滚动。
你们对如何实现这种滚动有任何想法吗?
我真的迷失了。任何帮助将非常感激它:(
谢谢!
同时处理滚动视图和表视图的解决方案围绕UIScrollViewDelegate
。因此,让您的视图控制器符合该协议:
class ViewController: UIViewController, UIScrollViewDelegate {
我将滚动视图和表视图表示为插座:
@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来演示结果:
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);
修改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
经过多次试验和错误,这对我来说最有效。解决方案必须解决两个需求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")
}
}
希望这有助于某人!
这里的答案都不适合我。每个人都有自己的细微差别问题(当一个滚动视图击中它的底部时需要重复滑动,或滚动指示器看起来不正确等等),所以我想再投入另一个答案。
Ole Begemann有一个很好的写作,正是这样做https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/
尽管是一个旧帖子,但这些概念仍然适用于当前的API。此外,还有一个维护(Xcode 9兼容)Objective-C实现他的方法https://github.com/eyeem/OLEContainerScrollView
我认为有两种选择。
由于您知道滚动视图和主视图的大小,因此您无法判断滚动视图是否触及底部。
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
来简单地控制活动视图。
我找到了一个很棒的图书馆MXParallaxHeader
在故事板中,只需将UIScrollView
类设置为MXScrollView
然后魔术就会发生。
当我嵌入UIScrollView
容器视图时,我用这个类来处理我的UIPageViewController
。即使您可以插入视差标题视图以获取更多细节。
此外,这个库提供Cocoapods
和Carthage
我附上了一张代表UIView
Hierarchy的图片。 MXScrollView Hierarchy
我也在努力解决这个问题。有一个非常简单的解决方案。
在界面构建器中:
现在,这里有趣的部分:对于您添加的每个页面,请确保其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背景颜色。您必须将其设置为清除,以便从tableView下可以看到标题。
我尝试将标记为正确答案的解决方案,但它无法正常工作。用户需要在表格视图上单击两次以进行滚动,之后我无法再次滚动整个屏幕。所以我只是在viewDidLoad()中应用了以下代码:
tableView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(tableViewSwiped)))
scrollView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(scrollViewSwiped)))
以下代码是动作的实现:
func tableViewSwiped(){
scrollView.isScrollEnabled = false
tableView.isScrollEnabled = true
}
func scrollViewSwiped(){
scrollView.isScrollEnabled = true
tableView.isScrollEnabled = false
}
也许是蛮力,但工作得很好:顺便说一下,我使用自动布局。
对于tableView(或collectionView或其他),在storyboard中设置任意高度,并为类创建一个出口。在适当的情况下,(viewDidLoad()或...)将tableView的高度设置得足够大,以便tableView不需要滚动。 (需要提前知道行数)然后只有外部scrollView才能很好地滚动。