Перемещение представления при прокрутке в UITableView
У меня есть UIView
с UITableView
ниже:
![enter image description here]()
То, что я хотел бы сделать, - это показать выше над UITableView
движение вверх (в сторону), когда пользователь начнет прокрутку в таблице, чтобы иметь больше места для UITableView
(и спуститься когда вы снова прокрутите страницу вниз).
Я знаю, что это обычно делается с представлением заголовка таблицы, но моя проблема заключается в том, что мое представление таблицы находится внутри вкладки (на самом деле это просмотр страницы с прокруткой страницы, реализованный с использованием TTSliddingPageviewcontroller
). Так что, хотя у меня есть только один топ UIView
, есть три UITableView
s.
Можно ли выполнить это вручную? Моя первая мысль состоит в том, чтобы поместить все в UIScrollView
, но, согласно документации Apple, никогда не следует размещать UITableView
внутри UIScrollView
, поскольку это приводит к непредсказуемому поведению.
Ответы
Ответ 1
Так как UITableView
является подклассом UIScrollView
, ваш делегат представления таблиц может получать методы 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;
}
Ответ 2
Решение для Swift (отлично работает с откатом для просмотра прокрутки):
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
}
Ответ 3
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
}
Ответ 4
Более простой и быстрый подход
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect rect = self.view.frame;
rect.origin.y = -scrollView.contentOffset.y;
self.view.frame = rect;
}
Ответ 5
Кто-то попросил код для моего решения, поэтому я отправляю его здесь как ответ. Кредит на эту идею должен по-прежнему идти к 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
- это переменная экземпляра CGFloat previousOffset;
.
topVerticalConstraint
является NSLayoutConstraint
, который задается как IBOutlet
. Он идет от вершины представления к вершине своего супервизора, а начальное значение равно 0.
Это не идеально. Например, если пользователь прокручивается очень агрессивно, движение взгляда может немного отрываться. Этот вопрос хуже для больших просмотров; если представление достаточно мало, проблем нет.
Ответ 6
Я знаю этот пост очень старым. Я пробовал решения, но ни один из них не работал у меня, потому что я старался использовать его, надеюсь, он может вам помочь. Этот сценарий довольно распространен, так как Apple предложила не использовать TableViewController внутри любого ScrollView, потому что компилятор будет смущен, как и в ком-то, чтобы ответить, потому что ему будет возвращен ответ на два делегата - один из 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, определяемый как свойство
Представленная иерархия выглядит следующим образом:
ViewController → View содержит ScrollView (метод делегирования которого указан выше) → Contain TableView.
В приведенном выше коде мы вручную увеличиваем и уменьшаем высоту представления таблицы вместе с размером содержимого ScrollView.
Не забудьте отключить прокрутку TableView.
Ответ 7
Чтобы создать такую анимацию,
![введите описание изображения здесь]()
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
}
вы должны изменить pageMenu.view с помощью коллекцииView/tableView
Ответ 8
Я добавил некоторые ограничения для последнего решения, чтобы предотвратить некоторые странные поведения в случае быстрой прокрутки
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
}