web-dev-qa-db-fra.com

UIScrollview programmatique avec autolayout

Après avoir lu les notes techniques sur le site Web d'Apple et lu le livre de matt neuburg sur la programmation d'iOS 11 avec UIScrollview maintenu en place avec Autolayout, je n'ai pas été en mesure de bien comprendre le concept de son fonctionnement. 

Ce que je veux fondamentalement, c'est un Scrollview qui aurait une vue enfant ChildView où cette vue enfant aurait alors un Textview.

Ci-dessous, j'ai joint la maquette de ce que je suis en train de réaliser. Programmatique no-nibs, no storyboards.

 enter image description here

et en ce qui concerne le code, voici ce que je propose habituellement:

Code

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 
}

Remarque: pour le ContentView, je définis normalement des contraintes pour ancrer les bords dans la vue de défilement, mais pas dans ce cas avec Autolayout et le fait que je souhaite le faire défiler verticalement clavier becomesFirstResponder. Une autre façon de travailler avec cette méthode consiste à créer une variable UIView qui s'étend sur une taille supérieure à la vue de défilement afin de permettre à la vue enfant de devenir une sous-vue de cette vue plus grande ayant la vue de défilement comme parent.

Mon problème: Comment puis-je atteindre cet objectif à partir de maintenant? Avez-vous des suggestions? J'ai réfléchi à quelque chose comme ceci: (ContentView serait la vue la plus large qui permettrait de faire défiler la vue, et la vue enfant serait la 3ème vue enfant de la hiérarchie)

 enter image description here

9
Samarey

J'ai lu attentivement la documentation d'Apple sur UIScrollView et en particulier son fonctionnement avec auto layout et je préfère aussi le chemin programmatique. Premièrement, vous n'avez pas besoin de créer une vue de contenu erronée, vous pouvez ajouter des sous-vues directement à la vue de défilement (et je préfère cela car je considère une vue erronée comme un objet étranger). Apple, autant que je sache, ne recommande pas d'en créer un, mais suggère simplement que vous le pouvez. Et, autant que je m'en souvienne, ils ont produit des exemples où ils ne le font pas. Deuxièmement, les sous-vues de la vue de défilement ne doivent pas (probablement ne pas) compter sur la vue de défilement pour déterminer leurs largeurs ou leurs hauteurs. Et troisièmement, vos contraintes doivent être chaînées de gauche à droite et de haut en bas pour la présentation automatique afin de créer la vue du contenu pour vous. La troisième "règle" peut paraître un peu déroutante ou même sembler paradoxale, mais c’est exactement comme cela que Apple souhaite que vous construisiez votre vue de défilement.

Pour être précis, lorsque vous créez une vue de défilement, vous pouvez donner à son cadre les limites de la vue du contrôleur:

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

Vous devez ensuite définir les limites de la vue de contenu en ancrant ses sous-vues aux bords de la vue de défilement. Par conséquent, votre vue la plus haute doit être ancrée en haut de la vue de défilement et sa largeur ne peut pas dépasser la largeur de la vue de défilement (qui est la largeur de la vue) (si votre objectif est le défilement vertical uniquement).

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

Remarquez que la topMostView ne compte pas sur la vue de défilement pour déterminer sa taille (uniquement sa position).

Le contenu de votre vue de défilement a maintenant une hauteur de 1000 mais il ne défilera pas car rien n’est ancré au bas de la vue de défilement. Par conséquent, faites cela dans votre vue la plus basse.

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

La dernière ancre peut sembler paradoxale car vous ancrez une vue dont le point 1000 correspond à une ancre que vous venez d'ancrer au bas de la vue et qui est nettement inférieure à la hauteur de 1000. Mais voici comment Apple veut que vous le fassiez. Avec cette approche, non seulement vous n’avez pas besoin de créer une vue de contenu, vous pouvez en fait prétendre qu’elle n’existe pas - sauf, bien sûr, si vous avez besoin de ses limites qui peuvent toujours être lues à partir des propriétés de la vue de défilement.

En fait, ce principe dépasse les vues de défilement. Lorsque vous créez une UITableViewCell personnalisée à l'aide de la disposition automatique, enchaînez vos contraintes de gauche à droite et de haut en bas, là où la sous-vue la plus en haut est ancrée en haut de la cellule (topMostView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true) et la sous-vue la plus basse en bas. (bottomMostView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true) et que vous avez des cellules à dimensionnement automatique.

18
bsod

Lorsque vous créez un scrollView, Apple vous recommande d'y placer un contentView, de lui donner la largeur de la vue du viewController et d'épingler ses contraintes supérieure, inférieure et inférieure au scrollview, puis de commencer par placer les éléments de haut en bas voulez et épinglez l'élément le plus en bas au bas de contentView de la vue de défilement, de sorte que la vue de défilement puisse afficher sa hauteur, cette contrainte de fond peut être comme vous le souhaitez et, selon elle, scrollview continue de défiler jusqu'à la fin. 

3
Sh_Khan