Мой Swift 4 UIScrollView с ограничениями авторазметки не прокручивается

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

У меня есть представление прокрутки, которое будет содержать несколько кнопок для горизонтальной прокрутки. Я знаю, что эти кнопки должны перейти в представление контейнера в представлении прокрутки и создали это также. Первоначально я использовал ограничения autolayout, чтобы создать все это, но теперь попытался использовать константы, чтобы гарантировать, что представление контента больше, чем представление прокрутки. Однако кнопки все равно не будут прокручиваться... я что-то пропустил? Виды прокрутки не работают с автоматической разметкой?

Я делаю все это программно и на своем iPad, так что решения с построителем интерфейса, к сожалению, не вариант...

Вот полный код:

import UIKit
import PlaygroundSupport

class FilterViewController: UIViewController {
    var filterView: UIView!
    var scrollView: UIScrollView!
    var containerView: UIView!

    override func loadView() {
        filterView = UIView()
        view = filterView
        view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)

        scrollView = UIScrollView()
        scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0)
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo:view.topAnchor, constant:40).isActive = true
        scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
        scrollView.widthAnchor.constraint(equalTo:view.widthAnchor).isActive = true
        scrollView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        scrollView.isScrollEnabled = true

        containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
        scrollView.addSubview(containerView)
        containerView.frame = CGRect(x: 0, y: 0, width: 1080, height: 200)
    }

    class Buttons{
        let button = UIButton()
        init (titleText : String){
            button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0)
            button.setTitle(titleText, for: .normal)
            button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let b1 = Buttons(titleText: "one")
        let b2 = Buttons(titleText: "two")
        let b3 = Buttons(titleText: "three")
        let b4 = Buttons(titleText: "four")
        let b5 = Buttons(titleText: "five")
        let buttonArray = [b1,b2,b3,b4,b5]
        var startPoint : CGFloat = 0.0
        for btn in buttonArray {
            let theBtn = btn.button
            containerView.addSubview(theBtn)
            theBtn.frame = CGRect(x: startPoint, y: 0, width: 200, height: 200)
            startPoint += 220
        }

    }
}

let filterViewController = FilterViewController()
PlaygroundPage.current.liveView = filterViewController

Спасибо ВАВАВАМА! Вот полный (работающий сейчас) мини-проект со всеми ограничениями автоматической компоновки:

import UIKit
import PlaygroundSupport

class FilterViewController: UIViewController {
    var filterView: UIView!
    var scrollView: UIScrollView!
    var containerView: UIView!

    override func loadView() {
        filterView = UIView()
        view = filterView
        view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)

        scrollView = UIScrollView()
        scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0)
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo:view.topAnchor, constant:40).isActive = true
        scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
        scrollView.widthAnchor.constraint(equalTo:view.widthAnchor).isActive = true
        scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25).isActive = true
        scrollView.isScrollEnabled = true

        containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
        scrollView.addSubview(containerView)
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.topAnchor.constraint(equalTo:scrollView.topAnchor).isActive = true
        containerView.leadingAnchor.constraint(equalTo:scrollView.leadingAnchor).isActive = true
        containerView.trailingAnchor.constraint(equalTo:scrollView.trailingAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo:scrollView.bottomAnchor).isActive = true
        containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true



    }

    class Buttons{
        let button = UIButton()
        init (titleText : String){
            button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0)
            button.setTitle(titleText, for: .normal)
            //button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let b1 = Buttons(titleText: "one")
        let b2 = Buttons(titleText: "two")
        let b3 = Buttons(titleText: "three")
        let b4 = Buttons(titleText: "four")
        let b5 = Buttons(titleText: "five")
        let buttonArray = [b1,b2,b3,b4,b5]
        var startPoint = containerView.leadingAnchor
        for btn in buttonArray {
            let theBtn = btn.button
            containerView.addSubview(theBtn)
            theBtn.translatesAutoresizingMaskIntoConstraints = false
            theBtn.leadingAnchor.constraint(equalTo:startPoint, constant:20).isActive = true
            theBtn.topAnchor.constraint(equalTo:containerView.topAnchor).isActive = true
            theBtn.bottomAnchor.constraint(equalTo:containerView.bottomAnchor).isActive = true
            theBtn.widthAnchor.constraint(equalTo: theBtn.heightAnchor).isActive = true
            startPoint = theBtn.trailingAnchor
            containerView.widthAnchor.constraint(equalTo: theBtn.widthAnchor, multiplier:CGFloat(buttonArray.count), constant: CGFloat(buttonArray.count * 20)).isActive = true
        }
    }
}

let filterViewController = FilterViewController()
PlaygroundPage.current.liveView = filterViewController

1 ответ

Решение

Вы можете сделать это с помощью Auto Layout. Секрет в том, чтобы ограничить края containerView к краям scrollView, Это не интуитивно понятно, но ограничивает края containerView не устанавливает размер, он просто гарантирует, что размер содержимого scrollView растет как containerView растет. Устанавливая ограничения для ширины containerView к константе, которая является большим числом, чем ширина scrollView, контент будет прокручиваться горизонтально.

Примечание: при настройке scrollView таким образом, вы не установите contentSize из scrollView, contentSize будет рассчитан для вас Auto Layout и будет равен размеру containerView, Важно убедиться, что размер containerView полностью определяется ограничениями.

Вот что я изменил, чтобы заставить это работать:

containerView = UIView()
containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
scrollView.addSubview(containerView)
//containerView.frame = CGRect(x: 0, y: 0, width: 1080, height: 200)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.topAnchor.constraint(equalTo:scrollView.topAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo:scrollView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo:scrollView.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo:scrollView.bottomAnchor).isActive = true
containerView.heightAnchor.constraint(equalToConstant: 200).isActive = true
containerView.widthAnchor.constraint(equalToConstant: 1080).isActive = true

Почему мой контент не прокручивается?

Для прокрутки containerView должен быть больше чем scrollView, Ваша ошибка в том, что вы установили ограничения таким образом, чтобы containerView такой же ширины и высоты, как scrollViewи именно поэтому ваш контент не прокручивается.

Если вы хотите прокрутить горизонтально, ширина containerView должен быть больше чем scrollViewширина Вы можете сделать это одним из двух способов:

  1. Укажите явную постоянную ширину для containerView это больше, чем scrollViewширина

    ИЛИ ЖЕ

  2. Цепочка подпредставлений containerView слева направо, причем крайний левый элемент находится на переднем крае containerView, Полностью укажите ширину подпредставлений и поместите ограничения по расстоянию между подпредставлениями. Крайнее правое подпредставление должно иметь смещение от задней кромки containerView, Делая это, Auto Layout может вычислить ширину containerView и установить contentSize из scrollView,


Мини проект: обновление

Это версия вашего мини-проекта, которая использует цепочку ограниченных представлений для определения containerViewширина Ключ является последним ограничением после for зациклиться viewDidLoad() который соединяет трейлинг-якорь последней кнопки (он же startPoint) к containerViewЗавязываю якорь. Это завершает цепочку противопоказаний и кнопок, которые соединяют передний край containerView с задней кромкой containerView, При этом Auto Layout может вычислить ширину containerView и установить contentSize из scrollView,

import UIKit
import PlaygroundSupport

class FilterViewController: UIViewController {
    var filterView: UIView!
    var scrollView: UIScrollView!
    var containerView: UIView!

    override func loadView() {
        filterView = UIView()
        view = filterView
        view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)

        scrollView = UIScrollView()
        scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0)
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25).isActive = true
        scrollView.isScrollEnabled = true

        containerView = UIView()
        containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0)
        scrollView.addSubview(containerView)
        containerView.translatesAutoresizingMaskIntoConstraints = false

        // This is key:  connect all four edges of the containerView to
        // to the edges of the scrollView
        containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

        // Making containerView and scrollView the same height means the
        // content will not scroll vertically
        containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true    
    }

    class Buttons {
        let button = UIButton()
        init(titleText: String) {
            button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0)
            button.setTitle(titleText, for: .normal)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let b1 = Buttons(titleText: "one")
        let b2 = Buttons(titleText: "two")
        let b3 = Buttons(titleText: "three")
        let b4 = Buttons(titleText: "four")
        let b5 = Buttons(titleText: "five")
        let buttonArray = [b1, b2, b3, b4, b5]
        var startPoint = containerView.leadingAnchor
        for btn in buttonArray {
            let theBtn = btn.button
            containerView.addSubview(theBtn)
            theBtn.translatesAutoresizingMaskIntoConstraints = false
            theBtn.leadingAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true
            theBtn.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
            theBtn.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
            theBtn.widthAnchor.constraint(equalTo: theBtn.heightAnchor).isActive = true
            startPoint = theBtn.trailingAnchor
        }
        // Complete the chain of constraints
        containerView.trailingAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true
    }
}

let filterViewController = FilterViewController()
PlaygroundPage.current.liveView = filterViewController
Другие вопросы по тегам