it-swarm.com.ru

Переместить вид при прокрутке в UITableView

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

enter image description here

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

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

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

23
pajevic

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

Решение для 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
 }
23
George Muntean

Свифт 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
    }
7
Dvole

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

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

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

}
6
Ghulam Rasool Hashmi

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

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
}
3
Exception

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

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

3
pajevic

Чтобы создать как эта анимация, 

 enter image description here

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

1
Yassine En

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

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

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

0
Rishabh Sanghvi