Использование UIScrollView с минимальным верхним якорем контента вызывает визуальный сбой - PullRequest
1 голос
/ 16 января 2020

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

Вот мой код:

class HomeParallaxScrollViewController: UIViewController {

    private let topImageView = UIImageView(image: UIImage(named: "cat"))
    private let contentView = UIView()
    private let scrollView = UIScrollView()
    private let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .gray

        topImageView.contentMode = .scaleAspectFill
        contentView.backgroundColor = .white
        label.text = "SOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT"
        label.textColor = .black
        label.numberOfLines = 0

        [contentView, label, topImageView, scrollView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false }

        scrollView.addSubview(contentView)
        contentView.addSubview(label)
        view.addSubview(topImageView)
        view.addSubview(scrollView)

        NSLayoutConstraint.activate([
            topImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
            topImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            topImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            topImageView.heightAnchor.constraint(equalToConstant: 200),

            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
            scrollView.topAnchor.constraint(equalTo: topImageView.bottomAnchor, constant: -30),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.topAnchor.constraint(lessThanOrEqualTo: topImageView.bottomAnchor), //This is what's causing the glitch

            label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            label.topAnchor.constraint(equalTo: contentView.topAnchor),
            label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
    }
}

И вот что происходит: enter image description here

1 Ответ

1 голос
/ 16 января 2020

Попытка добавить другое верхнее ограничение - особенно для элемента вне представления прокрутки - плохая идея, и, как вы видите, не сработает. Я уверен, что вы заметили, что генерируются конфликтные сообщения автоматического макета.

Один из подходов заключается в реализации scrollViewDidScroll делегирования удовольствия c:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    // limit drag-down in the scroll view to the overlap size
    scrollView.contentOffset.y = max(scrollView.contentOffset.y, -30)
}

Когда пользователь перетаскивает его вниз прокрутите, он остановится на 30 точках.

Вот ваш пример с небольшими изменениями - у меня нет ваших .plBackgroundLightGray или .PLSemiboldFont, и я добавил загрузку изображения для просмотра верхнего изображения - но это должно работать как есть:

// conform to UIScrollViewDelegate
class HomeParallaxScrollViewController: UIViewController, UIScrollViewDelegate {

    private let topImageView = UIImageView(image: UIImage(named: "cat"))
    private let contentView = UIView()
    private let scrollView = UIScrollView()
    private let label = UILabel()

    // this will be the "overlap" of the scroll view and top image view
    private var scrollOverlap: CGFloat = 30.0

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // limit drag-down in the scroll view to scrollOverlap points
        scrollView.contentOffset.y = max(scrollView.contentOffset.y, -scrollOverlap)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .lightGray // .plBackgroundLightGray

        topImageView.contentMode = .scaleAspectFill
        if let img = UIImage(named: "background") {
            topImageView.image = img
        }
        contentView.backgroundColor = .white
        label.text = "SOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT\n\n\nSOME\n\n\nRANDOM\n\n\nCONTENT"
        label.font = UIFont.boldSystemFont(ofSize: 16) // .PLSemiboldFont(size: 16)
        label.textColor = .black
        label.numberOfLines = 0

        [contentView, label, topImageView, scrollView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false }

        scrollView.addSubview(contentView)
        contentView.addSubview(label)
        view.addSubview(topImageView)
        view.addSubview(scrollView)

        NSLayoutConstraint.activate([
            topImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
            topImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            topImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            topImageView.heightAnchor.constraint(equalToConstant: 200),

            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
            scrollView.topAnchor.constraint(equalTo: topImageView.bottomAnchor, constant: scrollOverlap),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),

            // nope, not a good idea -- will cause constraint conflicts
            //contentView.topAnchor.constraint(lessThanOrEqualTo: topImageView.bottomAnchor), //This is what's causing the glitch

            label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            label.topAnchor.constraint(equalTo: contentView.topAnchor),
            label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])

        // set delegate to self
        scrollView.delegate = self
    }

}
...