Перемещение представления при прокрутке в 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
}