Изменить сенсорный приемник при прокрутке просмотра прокрутки - PullRequest
0 голосов
/ 20 мая 2018

В таких приложениях, как приложение Apple для карт или карты Google, используются прокручиваемые нижние листы для представления дополнительного контента.Хотя это поведение не так уж сложно перестроить, я изо всех сил пытаюсь реализовать одну важную функцию:

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

Вот пример видео, которое я имею в виду:

Пример видео:

Bottom Sheet Example

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


Чтобы добиться такого поведения, я вижу три разных подхода:

  1. Я отслеживаю contentOffset представления прокрутки содержимого в методе делегата scrollViewDidScroll(_:) представления прокрутки.Затем я делаю

    if contentScrollView.contentOffset.y < 0 {
        contentScrollView.contentOffset.y = 0
    }
    

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

  2. Я нахожу способ изменить приемник распознавателя жестов прокрутки (панорамирования) с представления прокрутки контента на просмотр супер прокрутки, как только прокрутка контента прокручивается до его верха.

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


Хотя мне удалось реализовать первый вариант (это то, что вы видите в видео), я бы настоятельно предпочел использовать подход 2 или 3. Это гораздо более чистый способчтобы контроллер представления, управляющий нижним листом, управлял всей логикой прокрутки, не раскрывая его внутренности.

К сожалению, я не нашел способа как-то разделить жест панорамирования на два компонента (один, который управляет приемником)представление прокрутки и одно, которое управляет другим представлением прокрутки)

Есть идеи, как добиться такого поведения?

1 Ответ

0 голосов
/ 23 мая 2018

Меня очень интересует этот вопрос, и я надеюсь, что, предоставив, как я его реализовал, он не подавит ответ, который может показать, как правильно обойти респондента.Уловка, которую я думаю, что я помещаю в комментарии, отслеживает прикосновения.Я забыл о том, как scrollview поглощает их, но вы можете использовать UIPanGesture.Посмотрите, близко ли это к тому, что вы ищете.Единственный случай, с которым я столкнулся, может потребовать больше размышлений - использовать свиток, чтобы отклонить вид снизу.Большая часть этого кода настроена для получения рабочего вида прокрутки в представлении.Я думаю, что лучше всего использовать анимацию свойств, чтобы сделать ее прерываемой, или даже мою любимую анимацию в Facebook Pop.Для простоты я использовал анимацию UIView.Дайте мне знать, если это решит то, что вы ищете. Код ниже и вот результат sogif.Вид прокрутки остается прокручиваемым и активным.Я анимирую кадры, но ограничения на обновление также могут работать.

import UIKit

    class ViewController: UIViewController{
        //setup
        var items : [Int] = []
        lazy var tableView : UITableView = {
            let tv = UITableView(frame: CGRect(x: 0, y: topViewHeight, width: self.view.frame.width, height: self.view.frame.height))
            tv.autoresizingMask = [.flexibleWidth,.flexibleHeight]
            tv.delegate = self
            tv.dataSource = self
            tv.layer.cornerRadius = 4
            return tv
        }()

        lazy var topView : UIView = {
            let v = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: topViewHeight))
            v.backgroundColor = .green
            v.autoresizingMask = [.flexibleWidth,.flexibleHeight]
            return v
        }()

        let cellIdentifier = "ourCell"



        //for animation
        var isAnimating = false
        var lastOffset : CGPoint = .zero
        var startingTouch : CGPoint?
        let topViewHeight : CGFloat = 500
        var isShowing : Bool = false
        let maxCollapse : CGFloat = 50




        override func viewDidLoad() {
            super.viewDidLoad()

            for x in 0...100{
                items.append(x)
            }
            // Do any additional setup after loading the view, typically from a nib.
            self.view.addSubview(topView)
            self.view.addSubview(tableView)
            self.tableView.reloadData()

            let pan = UIPanGestureRecognizer(target: self, action: #selector(moveFunction(pan:)))
            pan.delegate = self
            self.view.addGestureRecognizer(pan)
        }

        @objc func moveFunction(pan:UIPanGestureRecognizer) {
            let point:CGPoint = pan.location(in: self.view)
            switch pan.state {
            case .began:
                startingTouch = point
                break
            case .changed:
                  processMove(touchPoint:point.y)
                break
            default:
                processEnding(currentPointY: point.y)
                break
            }
        }

    }

    extension ViewController : UITableViewDelegate,UITableViewDataSource {

        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }

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

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            var cell : UITableViewCell!
            cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
            if cell == nil {
                cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)
            }
            cell.textLabel?.text = "\(items[indexPath.row])"
            return cell
        }

        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return 30
        }
    }

    extension ViewController : UIScrollViewDelegate{
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            if isAnimating == true{
                scrollView.contentOffset = lastOffset
                return
            }
            lastOffset = scrollView.contentOffset
        }
    }

    extension ViewController : UIGestureRecognizerDelegate{
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
    }

    extension ViewController{
        func processMove(touchPoint:CGFloat){
            if let start = startingTouch{
                if touchPoint <= topViewHeight && start.y > topViewHeight{
                    isAnimating = true
                    tableView.frame = CGRect(x: 0, y:touchPoint, width: self.view.frame.width, height: self.view.frame.height)
                    return
                }else if touchPoint >= self.maxCollapse && isShowing == true && start.y < self.maxCollapse{
                    isAnimating = true
                    tableView.frame = CGRect(x: 0, y:touchPoint, width: self.view.frame.width, height: self.view.frame.height)
                    return
                }else if isShowing == true && self.tableView.contentOffset.y <= 0{
                    //this is the only one i am slightly unsure about
                    isAnimating = true
                    tableView.frame = CGRect(x: 0, y:touchPoint, width: self.view.frame.width, height: self.view.frame.height)
                    return
                }
            }
            self.isAnimating = false
        }

        func processEnding(currentPointY:CGFloat){
            startingTouch = nil

            if isAnimating{
                if currentPointY < topViewHeight/2{
                    UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: {
                        self.tableView.frame = CGRect(x: 0, y:self.maxCollapse, width: self.view.frame.width, height: self.view.frame.height)
                    }) { (finished) in
                        self.isShowing = true
                    }
                }else{
                    UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: {
                        self.tableView.frame = CGRect(x: 0, y:self.topViewHeight, width: self.view.frame.width, height: self.view.frame.height)
                    }) { (finished) in
                        self.isShowing = false

                    }
                }
            }
            self.isAnimating = false
        }
    }
...