Перемещение вида при прокрутке в UITableView



У меня есть UIView с UITableView под ним:



Введите описание изображения здесь



Я хотел бы сделать так, чтобы вид над UITableView перемещался вверх (в сторону), когда пользователь начинает прокручивать таблицу, чтобы иметь больше места для UITableView (и опускался, когда вы снова прокручиваете вниз).



Я знаю, что это обычно делается с помощью представления заголовка таблицы, но моя проблема заключается в том, что мое представление таблицы находится внутри вкладки (на самом деле это представление страницы с боковой прокруткой, реализованное с помощью TTSliddingPageviewcontroller). Так в то время как у меня есть только одна вершина UIView , Есть три UITableViews.

Можно ли сделать это вручную? Моя первая мысль-поместить все в UIScrollView, но, согласно документации Apple, никогда не следует помещать UITableView внутрь UIScrollView, поскольку это приводит к непредсказуемому поведению.

587   8  

8 ответов:

Поскольку 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;
}

Решение для Swift (отлично работает с bounce, включенным для просмотра прокрутки):

 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
 }

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
    }

Более простой и быстрый подход

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{

    CGRect rect = self.view.frame;
    rect.origin.y =  -scrollView.contentOffset.y;
    self.view.frame = rect;

}

Я добавил некоторые ограничения к последнему решению, чтобы предотвратить некоторые странные поведения в случае быстрой прокрутки

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
}

Кто-то попросил код для моего решения, поэтому я размещаю его здесь в качестве ответа. Заслуга в этой идее по-прежнему должна принадлежать 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.

Это не идеально. Например, если пользователь очень агрессивно прокручивает вверх, движение представления может стать немного резким. Проблема хуже для больших представлений; если представление достаточно мало, то нет никаких проблем.

Чтобы создать подобную анимацию,

Введите описание изображения здесь

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.просмотр с помощью вашего collectionView/tableView

Я знаю этот пост очень давно. Я пробовал вышеперечисленные решения, но ни одно из них не сработало для меня, чтобы попробовать свои собственные, надеюсь, это может помочь вам. Этот сценарий довольно распространен, так как 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, определяемый как свойство

Вид Heirarchy выглядит следующим образом: Файл ViewController --> представление содержит представление ScrollView (делегат метод которого определяется выше) --> содержит TableView для.

С помощью приведенного выше кода мы вручную увеличиваем и уменьшаем высоту табличного представления вместе с размером содержимого ScrollView.

Не забудьте отключить прокрутку TableView.

Comments

    Ничего не найдено.