Подтвердить что ты не робот

Как заставить свиток TableView внутри ScrollView вести себя естественным образом

Мне нужно сделать это приложение со странной конфигурацией.

Как показано на следующем изображении, основным видом является UIScrollView. Затем внутри он должен иметь UIPageView, и каждая страница PageView должна иметь UITableView.

figure

Я сделал все это до сих пор. Но моя проблема в том, что я хочу, чтобы прокрутка вела себя естественно.

Следующее - это то, что я имею в виду естественно. В настоящее время, когда я просматриваю один из UITableViews, он прокручивает табличное представление (а не прокрутку). Но я хочу, чтобы она прокручивала ScrollView, если прокрутка не может прокручиваться, потому что она попала в ее верхнюю или нижнюю часть (в этом случае мне бы хотелось прокрутить табличное представление).

Например, скажем, мой scrollview в настоящее время прокручивается вверх. Затем я нажимаю палец над таблицей (текущей отображаемой страницы) и начинаю прокручивать вниз. В этом случае я хочу прокрутить прокрутку (нет таблицы). Если я продолжаю прокручивать свой scrollview, и он достигнет дна, если я удалю палец с дисплея и верну его обратно по телу и прокручу вниз, я хочу, чтобы мое табличное представление прокручивалось вниз, потому что scrollview достиг своего дна, и это не возможность прокрутки.

Есть ли у вас какие-либо идеи о том, как реализовать эту прокрутку?

Я ДЕЙСТВИТЕЛЬНО проиграл это. Любая помощь будет очень признательна: (

Спасибо!

4b9b3361

Ответ 1

Решение одновременной обработки вида прокрутки и представления таблицы вращается вокруг UIScrollViewDelegate. Поэтому, ваш контроллер просмотра соответствует этому протоколу:

class ViewController: UIViewController, UIScrollViewDelegate {

Ill представляет вид прокрутки и вид таблицы как выходы:

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!

Нужно также отслеживать высоту содержимого прокрутки, а также высоты экрана. Вы увидите, почему позже.

let screenHeight = UIScreen.mainScreen().bounds.height
let scrollViewContentHeight = 1200 as CGFloat

В viewDidLoad: требуется небольшая конфигурация:

override func viewDidLoad() {
    super.viewDidLoad()

    scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight)
    scrollView.delegate = self
    tableView.delegate = self 
    scrollView.bounces = false
    tableView.bounces = false
    tableView.scrollEnabled = false
}

где Ive отключил подпрыгивание, чтобы все было просто. Параметры ключа - это делегаты для представления прокрутки и представления таблицы, а сначала прокрутка прокрутки таблицы отключена.

Это необходимо, чтобы метод делегата scrollViewDidScroll: мог обрабатывать доступ к нижней части прокрутки и достигать вершины представления таблицы. Вот этот метод:

func scrollViewDidScroll(scrollView: UIScrollView) {
    let yOffset = scrollView.contentOffset.y

    if scrollView == self.scrollView {
        if yOffset >= scrollViewContentHeight - screenHeight {
            scrollView.scrollEnabled = false
            tableView.scrollEnabled = true
        }
    }

    if scrollView == self.tableView {
        if yOffset <= 0 {
            self.scrollView.scrollEnabled = true
            self.tableView.scrollEnabled = false
        }
    }
}

То, что делает метод делегата, - это обнаружение, когда представление прокрутки достигло своего дна. Когда это произойдет, просмотр таблицы можно прокрутить. Он также обнаруживает, когда представление таблицы достигает вершины, где просмотр прокрутки снова включен.

Я создал GIF для демонстрации результатов:

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

Ответ 2

Изменен ответ Дэниела, чтобы сделать его более эффективным и без ошибок.

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableHeight: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    //Set table height to cover entire view
    //if navigation bar is not translucent, reduce navigation bar height from view height
    tableHeight.constant = self.view.frame.height-64
    self.tableView.isScrollEnabled = false
    //no need to write following if checked in storyboard
    self.scrollView.bounces = false
    self.tableView.bounces = true
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 20
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30))
    label.text = "Section 1"
    label.textAlignment = .center
    label.backgroundColor = .yellow
    return label
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.textLabel?.text = "Row: \(indexPath.row+1)"
    return cell
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView == self.scrollView {
        tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200)
    }

    if scrollView == self.tableView {
        self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
    }
}

Полный проект можно увидеть здесь: https://gitlab.com/vineetks/TableScroll.git

Ответ 3

После многих испытаний и ошибок это самое лучшее для меня. Решение должно решить две потребности: 1) определить, кто должен использовать свойство прокрутки; tableView или scrollView? 2) убедитесь, что tableView не предоставляет полномочия scrollView до тех пор, пока он не достигнет вершины таблицы/содержимого.

Чтобы проверить, следует ли использовать scrollview для прокрутки vs tableview, я проверил, был ли UIView прямо над моим табличным представлением в рамке. Если UIView находится в рамке, можно с уверенностью сказать, что scrollView должен иметь полномочия для прокрутки. Если UIView не находится внутри фрейма, это означает, что tableView занимает все окно, и для этого необходимо иметь полномочия для прокрутки.

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    if scrollView.bounds.intersects(UIView.frame) == true {
     //the UIView is within frame, use the UIScrollView scrolling.   

        if tableView.contentOffset.y == 0 {
            //tableViews content is at the top of the tableView.

        tableView.isUserInteractionEnabled = false
       tableView.resignFirstResponder()
        print("using scrollView scroll")

        } else {

            //UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView.

            tableView.isUserInteractionEnabled = true
            scrollView.resignFirstResponder()
            print("using tableView scroll")
        }

    } else {

        //UIView is not in frame. Use tableViews scroll.

        tableView.isUserInteractionEnabled = true
       scrollView.resignFirstResponder()
        print("using tableView scroll")

    }

}

надеюсь, что это поможет кому-то!

Ответ 4

Я думаю, что есть два варианта.

Поскольку вы знаете размер прокрутки и основного вида, вы не можете определить, попало ли прокрутка вниз или нет.

if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
    // reach bottom
}

Итак, когда он попал; вы в основном устанавливаете

[contentScrollView setScrollEnabled:NO];

и наоборот для вашего tableView.

Другое, что более точно, я думаю, это добавить Gesture к вашим взглядам.

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToTapGesture:)];

// Specify that the gesture must be a single tap
tapRecognizer.numberOfTapsRequired = 1;

// Add the tap gesture recognizer to the view
[self.view addGestureRecognizer:tapRecognizer];

// Do any additional setup after loading the view, typically from a nib

Итак, когда вы добавляете Gesture, вы можете просто управлять активным видом, изменяя setScrollEnabled в respondToTapGesture.

Ответ 5

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

Оле Бегеманн отлично написал о том, как именно это сделать https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/

Несмотря на то, что это старый пост, концепции все еще применимы к текущим API. Кроме того, существует поддерживаемая (совместимая с Xcode 9) реализация Objective-C его подхода https://github.com/eyeem/OLEContainerScrollView

Ответ 6

Я попробовал решение, помеченное как правильный ответ, но он не работал должным образом. Пользователю необходимо дважды щелкнуть по экрану таблицы для прокрутки, после чего я не смог снова прокрутить весь экран. Поэтому я просто применил следующий код в viewDidLoad():

tableView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(tableViewSwiped)))
scrollView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(scrollViewSwiped)))

И приведенный ниже код - это реализация действий:

func tableViewSwiped(){
    scrollView.isScrollEnabled = false
    tableView.isScrollEnabled = true
}

func scrollViewSwiped(){
    scrollView.isScrollEnabled = true
    tableView.isScrollEnabled = false
}

Ответ 7

Я нашел классную библиотеку MXParallaxHeader

В Storyboard просто установите класс UIScrollView в MXScrollView тогда волшебство случается.

Я использовал этот класс для обработки моего UIScrollView когда я встраиваю контейнерное представление UIPageViewController. даже вы можете вставить представление заголовка параллакса для более подробной информации.

Кроме того, эта библиотека предоставляет Cocoapods и Carthage

Я приложил изображение ниже, которое представляет UIView Hierarchy. Иерархия MXScrollView

Ответ 8

Может быть, грубой силой, но работает отлично: кстати, я использую автоматическую разметку.

для tableView (или collectionView или чего-либо еще) установите произвольную высоту в раскадровке и создайте выход для класса. Где это уместно, (viewDidLoad() или...) задайте высоту tableView, достаточно большую, чтобы tableView не нужно было прокручивать. (нужно заранее знать количество строк). Тогда только внешний scrollView будет хорошо прокручиваться.

Ответ 9

Я тоже боролся с этой проблемой. Есть очень простое решение.

В конструкторе интерфейса:

  1. создать простой ViewController
  2. добавить простой вид, это будет наш заголовок, и ограничить его суперпредставлением
    • это красный вид на примере ниже
    • Я добавил 12px сверху, слева и справа и установил фиксированную высоту 128px
  3. встроить PageViewController, убедившись, что он ограничен суперпредставлением, а не заголовком

ib

Теперь самое интересное: для каждой добавляемой страницы убедитесь, что ее tableView имеет смещение от вершины. Это оно. Вы можете сделать, например, с помощью этого кода (при условии, что вы используете UITableViewController в качестве страницы):

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let tables = viewControllers.compactMap { $0 as? UITableViewController }
    tables.forEach {
        $0.tableView.contentInset = UIEdgeInsets(top: headerView.bounds.height, left: 0, bottom: 0, right: 0)
        $0.tableView.contentOffset = CGPoint(x: 0, y: -headerView.bounds.height)
    }
}

Никакой беспорядочной прокрутки внутри прокрутки в табличном представлении, никакого искажения с делегатами, никаких дублированных прокруток, совершенно естественного поведения. Если вы не видите заголовок, возможно, это из-за цвета фона tableView. Вы должны установить его для очистки, чтобы заголовок был виден из-под tableView.

Ответ 10

CGFloat tableHeight = 0.0f;

YourArray =[response valueForKey:@"result"];

tableHeight = 0.0f;
for (int i = 0; i < [YourArray count]; i ++) {
    tableHeight += [self tableView:self.aTableviewDoc heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}

self.aTableviewDoc.frame = CGRectMake(self.aTableviewDoc.frame.origin.x, self.aTableviewDoc.frame.origin.y, self.aTableviewDoc.frame.size.width, tableHeight);