在UITableView中滚动时移动视图

我有一个UITableView下面的UITableView

在这里输入图像说明

我想要做的是当用户开始在表中滚动以便为UITableView留出更多的空间(当你再次向下滚动时下来)时,让UITableView上方的视图向上移动( UITableView )。

我知道这通常是使用表头视图,但我的问题是,我的表视图是在一个选项卡(实际上是一个使用TTSliddingPageviewcontroller实现的TTSliddingPageviewcontroller卷页面视图)。 所以,虽然我只有一个顶级UIView有三个UITableView

手动完成这个可能吗? 我的第一个想法是把所有东西放在UIScrollView ,但根据苹果的文档,不应该在UIScrollView放置一个UITableView因为这会导致不可预知的行为。

由于UITableViewUIScrollView的子类,因此您的表视图的委托可以接收UIScrollViewDelegate方法。

在你的表格视图的委托中:

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { static CGFloat previousOffset; CGRect rect = self.view.frame; rect.origin.y += previousOffset - scrollView.contentOffset.y; previousOffset = scrollView.contentOffset.y; self.view.frame = rect; } 

Swift的解决scheme(与滚动视图启用反弹完美结合):

  var oldContentOffset = CGPointZero let topConstraintRange = (CGFloat(120)..<CGFloat(300)) func scrollViewDidScroll(scrollView: UIScrollView) { let delta = scrollView.contentOffset.y - oldContentOffset.y //we compress the top view if delta > 0 && topConstraint.constant > topConstraintRange.start && scrollView.contentOffset.y > 0 { topConstraint.constant -= delta scrollView.contentOffset.y -= delta } //we expand the top view if delta < 0 && topConstraint.constant < topConstraintRange.end && scrollView.contentOffset.y < 0{ topConstraint.constant -= delta scrollView.contentOffset.y -= delta } oldContentOffset = scrollView.contentOffset } 

更简单快捷的方法

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGRect rect = self.view.frame; rect.origin.y = -scrollView.contentOffset.y; self.view.frame = rect; } 

Swift 3&4:

  var oldContentOffset = CGPoint.zero let topConstraintRange = (CGFloat(0)..<CGFloat(140)) func scrollViewDidScroll(_ scrollView: UIScrollView) { let delta = scrollView.contentOffset.y - oldContentOffset.y //we compress the top view if delta > 0 && yourConstraint.constant > topConstraintRange.lowerBound && scrollView.contentOffset.y > 0 { yourConstraint.constant -= delta scrollView.contentOffset.y -= delta } //we expand the top view if delta < 0 && yourConstraint.constant < topConstraintRange.upperBound && scrollView.contentOffset.y < 0{ yourConstraint.constant -= delta scrollView.contentOffset.y -= delta } oldContentOffset = scrollView.contentOffset } 

有人问我的解决scheme的代码,所以我把它张贴在这里作为答案。 这个想法的功劳还是应该去NobodyNada。

在我的UITableViewController我实现了这个委托方法:

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [[NSNotificationCenter defaultCenter] postNotificationName:@"TableViewScrolled" object:nil userInfo:scrollUserInfo]; } 

scrollUserInfo是一个NSDictionary ,我把我的UITableView传递给它的通知(我这样做viewDidLoad所以我只需要做一次):

 scrollUserInfo = [NSDictionary dictionaryWithObject:self.tableView forKey:@"scrollView"]; 

现在,在视图控制器有视图我想滚动屏幕,而我在viewDidLoad

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleScroll:) name:@"TableViewScrolled" object:nil]; 

最后我有这个方法:

 - (void)handleScroll:(NSNotification *)notification { UIScrollView *scrollView = [notification.userInfo valueForKey:@"scrollView"]; CGFloat currentOffset = scrollView.contentOffset.y; CGFloat height = scrollView.frame.size.height; CGFloat distanceFromBottom = scrollView.contentSize.height - currentOffset; if (previousOffset < currentOffset && distanceFromBottom > height) { if (currentOffset > viewHeight) currentOffset = viewHeight; self.topVerticalConstraint.constant += previousOffset - currentOffset; previousOffset = currentOffset; } else { if (previousOffset > currentOffset) { if (currentOffset < 0) currentOffset = 0; self.topVerticalConstraint.constant += previousOffset - currentOffset; previousOffset = currentOffset; } } } 

previousOffset是一个实例variablesCGFloat previousOffset;topVerticalConstraint是一个设置为IBOutletNSLayoutConstraint 。 它从视图顶部到其顶视图的顶部,初始值为0。

这并不完美。 例如,如果用户非常积极地滚动视图的运动可能会有点生涩。 对于大观点来说,这个问题更糟糕。 如果视图足够小,没有问题。

我很熟悉这篇文章。 我尝试了以上的解决scheme,但都没有为我自己尝试,希望它可以帮助你。 这种情况是非常普遍的,因为苹果build议不要在任何ScrollView中使用TableViewController,因为编译器会感到困惑,因为在哪个应答中,会得到两个委托callback – 一个来自ScrollViewDelegate,另一个来自UITableViewDelegate。

相反,我们可以使用ScrollViewDelegate并禁用UITableViewScrolling。

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat currentOffSetY = scrollView.contentOffset.y; CGFloat diffOffset = self.lastContentOffset - currentOffSetY; self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 400 + [self.tableView contentSize].height); if (self.lastContentOffset < scrollView.contentOffset.y) { tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y , tableView.frame.size.width, tableView.size.height - diffOffset); } if (self.lastContentOffset > scrollView.contentOffset.y) { tableView.frame = CGRectMake(tableView.frame.origin.x, tableViewframe.origin.y, tableViewframe.size.width, tableView.frame.size.height + diffOffset); } self.lastContentOffset = currentOffSetY; } 

这里lastContentOffset是CGFloat定义的属性

View Heirarchy如下:ViewController – > View包含ScrollView(其代表方法在上面定义) – >包含TableView。

通过上面的代码,我们手动增加和减less表视图的高度以及ScrollView的内容大小。

记住要禁用TableView的滚动。

为了创build像这个animation,

在这里输入图像说明

 lazy var redView: UIView = { let view = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 100)) view.backgroundColor = .red return view }() var pageMenu: CAPSPageMenu? override func viewDidLoad() { super.viewDidLoad() self.view.addSubview(redView) let rect = CGRect(x: 0, y: self.redView.frame.maxY, width: self.view.bounds.size.width, height:(self.view.bounds.size.height - (self.redView.frame.maxY))) pageMenu?.view.frame = rect self.view.addSubview(pageMenu!.view) } override func scrollViewDidScroll(_ scrollView: UIScrollView) { let offset = scrollView.contentOffset.y if(offset > 100){ self.redView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 0) }else{ self.redView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 100 - offset) } let rect = CGRect(x: 0, y: self.redView.frame.maxY, width: self.view.bounds.size.width, height:(self.view.bounds.size.height - (self.redView.frame.maxY))) pageMenu?.view.frame = rect } 

你必须改变你的collectionView / tableView pageMenu.view

我在最后的解决scheme中添加了一些限制,以防止在快速滚动的情况下出现一些奇怪的行为

 func scrollViewDidScroll(_ scrollView: UIScrollView) { let delta = scrollView.contentOffset.y - oldContentOffset.y //we compress the top view if delta > 0 && topConstraint.constant > topConstraintRange.lowerBound && scrollView.contentOffset.y > 0 { searchHeaderTopConstraint.constant = max(topConstraintRange.lowerBound, topConstraint.constant - delta) scrollView.contentOffset.y -= delta } //we expand the top view if delta < 0 && topConstraint.constant < topConstraintRange.upperBound && scrollView.contentOffset.y < 0 { topConstraint.constant = min(searchHeaderTopConstraint.constant - delta, topConstraintRange.upperBound) scrollView.contentOffset.y -= delta } oldContentOffset = scrollView.contentOffset }