Программный UIScrollview с автозапуском

После прочтения технических заметок на веб-сайте Apple и чтения книги Мэтта Нойбурга о программировании iOS 11 с помощью UIScrollview, поддерживаемой Autolayout, я не смог полностью понять концепцию того, как все это работает.

По сути, я хочу иметь Scrollview, у которого будет дочернее представление ChildView, где у этого дочернего представления будет Textview.

Ниже я приложил макет того, чего я пытаюсь достичь Программно, никаких раскадровок, никаких раскадровок.

enter image description here

и что касается кода, это то, что я обычно придумываю:

Код

let Scroller: UIScrollView = {
    let scroll = UIScrollView()
    scroll.translatesAutoresizingMaskIntoConstraints = false
    scroll.backgroundColor = UIColor.alizarinColor()
    return scroll
}()

// Content view

let ContentView : UIView = {

    let content = UIView()
    content.translatesAutoresizingMaskIntoConstraints = false
    content.backgroundColor = UIColor.blue
    return content
}()

override func viewDidLoad() {

    super.viewDidLoad()

    self.view.addSubview(Scroller)

    // Auto layout
    Scroller.leftAnchor.constraint(equalTo: view.leftAnchor, constant:0).isActive = true
    Scroller.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
    Scroller.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
    Scroller.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true

    Scroller.addSubview(ContentView)
    // Undefined Content view 
}

Обратите внимание: для ContentView я обычно определяю ограничения для привязки краев внутри scrollview, но не в этом случае с Autolayout и тем, что я хочу, чтобы он прокручивался вертикально вверх, когда клавиатура becomesFirstResponder. Другой способ, с помощью которого я попытался поработать, - это создать UIView который охватывает больший размер, чем Scrollview, чтобы позволить дочернему представлению быть подпредставлением этого более крупного представления, которое имеет представление прокрутки в качестве родителя.

Моя проблема: как я могу добиться этого начиная с этого момента? Какие-либо предложения? Я думал о чем-то вроде этого: (ContentView будет большим представлением, которое позволит прокручивать его, а дочернее представление будет третьим дочерним представлением в иерархии)

enter image description here

Ответ 1

Я внимательно прочитал документы Apple о том, как UIScrollView работает с автоматической разметкой.

  1. Во-первых, вам не нужно создавать представление с искусственным контентом, вы можете добавлять подпредставления непосредственно в представление с прокруткой (и я предпочитаю это, поскольку считаю представление с искусственным контентом посторонним и ненужным объектом). Apple не рекомендует создавать один, они только предлагают, что вы можете.

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

  3. И в-третьих, ваши ограничения должны определять самые левые, самые правые, самые верхние и самые нижние края, чтобы автоматическое расположение создавало представление контента для вас. Поначалу это правило может показаться нелогичным, но именно так Apple хочет, чтобы вы создали представление прокрутки.

В частности, когда вы создаете представление прокрутки, вы можете дать его фрейму границы представления контроллера:

scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true

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

topMostView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(topMostView)
topMostView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
topMostView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
topMostView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
topMostView.heightAnchor.constraint(equalToConstant: 1000).isActive = true

Обратите внимание, что topMostView не использует представление прокрутки для определения его размера (только его положение) - это важно.

Содержание в вашем представлении прокрутки теперь имеет высоту 1000 но оно не будет прокручиваться, потому что ничто не привязано к нижней части представления прокрутки. Поэтому сделайте это в своем самом нижнем представлении.

bottomMostView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(bottomMostView)
bottomMostView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
bottomMostView.topAnchor.constraint(equalTo: topMostView.bottomAnchor).isActive = true
bottomMostView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
bottomMostView.heightAnchor.constraint(equalToConstant: 1000).isActive = true

bottomMostView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

Последний якорь может показаться странным, потому что вы привязываете представление, которое имеет высоту 1000 пунктов, к якору, который вы только что прикрепили к нижней части вида, который определенно имеет высоту менее 1000 пунктов. Но именно так Apple хочет, чтобы вы это сделали. Таким образом, вам не нужно создавать представление контента, авто макет сделает это за вас. И вы, конечно, можете по-прежнему получать доступ к измерениям представления содержимого из свойств представления прокрутки как обычно.

Кстати, этот принцип определения "краевых ограничений" (самый левый, самый правый, самый верхний, самый нижний) выходит за рамки прокрутки. Когда вы создаете пользовательский UITableViewCell с помощью автоматического макета, определяете четыре граничных ограничения (т. topMostView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true привязано к верхней части ячейки topMostView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true, bottom Самое нижнее представление в нижней части ячейки bottomMostView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true и т.д.) - это способ создания ячеек с bottomMostView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true. Это то, как вы создаете что-либо для себя, например, представления, элементы управления и т.д.

Ответ 2

Когда вы создаете scrollView, apple рекомендует помещать в него содержимое contentView и давать этому contentView ширину viewController и привязывать ее вверху, внизу, впереди, привязывая ограничения к scrollview, а затем начинать с размещения элементов сверху донизу, поскольку вы хотите и прикрепить нижний элемент к нижней части scrollview contentView, поэтому scrollview может отображать его высоту, это нижнее ограничение может быть как вам угодно, и в соответствии с этим scrollview продолжит прокрутку, пока не закончит его