2017-03-18 1 views
0

J'ai ajouté une sous-vue scrollview dans l'une de mes vues, mais j'ai du mal à obtenir sa hauteur pour qu'elle corresponde exactement au contenu affiché par le scrollview, qui est du texte dans UILabel. La hauteur doit être dynamique (c'est-à-dire un facteur de la longueur du texte), car j'instancie cette vue pour de nombreuses longueurs de texte différentes. Chaque fois que je me connecte label.frame.bounds, je récupère (0,0). J'ai également essayé sizeToFits() dans quelques endroits sans beaucoup de chance.Comment dimensionner un UIScrollView pour ajuster une quantité inconnue de texte dans un UILabel?

Mon but est d'obtenir la fin de la scrollview quand elle atteint la dernière ligne de texte. En outre, j'utilise uniquement des contraintes programmatiques.

Une version condensée de mon code est le suivant:

import UIKit 

class ViewController: UIViewController, UIScrollViewDelegate { 
    let scrollView = UIScrollView() 
    let containerView = UIView() 
    let label = UILabel() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     scrollView.delegate = self 

     // This needs to change 
     scrollView.contentSize = CGSize(width: 375, height: 1000) 

     scrollView.addSubview(containerView) 
     view.addSubview(scrollView) 

     label.text = unknownAmountOfText() 
     label.backgroundColor = .gray 


     containerView.isUserInteractionEnabled = true 
     containerView.addSubview(label) 

     label.translatesAutoresizingMaskIntoConstraints = false 

     label.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true 

     label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 
    } 

    override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     scrollView.frame = view.bounds 
     containerView.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height) 
    } 
} 

Toute aide est appréciée.

solution trouvée:

func heightForLabel(text: String, font: UIFont, lineHeight: CGFloat, width: CGFloat) -> CGFloat { 
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)) 
    label.numberOfLines = 0 
    label.lineBreakMode = NSLineBreakMode.byWordWrapping 
    label.font = font 
    label.text = text 
    label.setLineHeight(lineHeight: lineHeight) 
    label.sizeToFit() 
    return label.frame.height 
} 

Je trouve cette solution en ligne, qui me donne ce que je dois définir la taille du contenu approprié pour la hauteur ScrollView fonction de la taille de l'étiquette. Idéalement, je serais capable de le déterminer sans cette fonction, mais pour l'instant je suis satisfait.

+0

Pourquoi faites-vous ce programme plutôt que dans le constructeur d'interface? Le moyen le plus simple de gérer cela est d'utiliser des contraintes de mise en page, bien que cela puisse devenir très vite laid dans le code. –

+0

Pourquoi mettez-vous UILabel dans un UIScrollView, puis essayez de redimensionner la vue de défilement pour l'adapter à l'étiquette? Voulez-vous faire défiler? Si oui, pourquoi redimensionner l'affichage déroulant? Si vous ne voulez pas faire défiler, pourquoi mettre l'étiquette dans une vue déroulante? – DonMag

+0

Je voulais essayer un projet uniquement avec des mises en page programmatiques. Je pense que dans mon prochain projet j'utiliserai l'interface constructeur. Mais pour l'instant, je suis trop profond dans ce projet pour changer, donc j'ai besoin de comprendre cela par programme. –

Répondre

1

La clé de UIScrollView et sa taille de contenu est la définition de vos contraintes afin que le contenu définit le contentSize. Pour un exemple simple: disons que vous avez un UIScrollView avec largeur: 200 et hauteur: 200. Maintenant, vous y mettez un UIView, qui a une largeur de 100 et une hauteur de 400. La vue doit défiler vers le haut et vers le bas, mais pas gauche-droite. Vous pouvez contraindre la vue à 100x400, puis "épingler" le haut, le bas, la gauche et la droite sur les côtés de la vue de défilement, et AutoLayout définira "automatiquement" le contenu de la scrollview.

Lorsque vous ajoutez subviews qui peuvent changer la taille - soit explicitement (code, interaction utilisateur) ou implicitement - si les contraintes sont correctement définies ces changements seront également « auto-magie » ajuster contentSize du scrollview.

Alors ... voici un exemple de ce que vous essayez de faire:

import UIKit 

class ViewController: UIViewController, UIScrollViewDelegate { 

    let scrollView = UIScrollView() 
    let label = UILabel() 

    let s1 = "1. This is the first line of text in the label. It has words and punctuation, but no embedded line-breaks, so what you see here is normal UILabel word-wrapping." 
    var counter = 1 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // turn off translatesAutoresizingMaskIntoConstraints, because we're going to set them 
     scrollView.translatesAutoresizingMaskIntoConstraints = false 
     label.translatesAutoresizingMaskIntoConstraints = false 

     // set background colors, just so we can see the bounding boxes 
     self.view.backgroundColor = UIColor(red: 1.0, green: 0.7, blue: 0.3, alpha: 1.0) 
     scrollView.backgroundColor = UIColor(red: 0.8, green: 0.8, blue: 1.0, alpha: 1.0) 
     label.backgroundColor = UIColor(white: 0.9, alpha: 1.0) 

     // add the label to the scrollView, and the scrollView to the "main" view 
     scrollView.addSubview(label) 
     self.view.addSubview(scrollView) 

     // set top, left, right constraints on scrollView to 
     // "main" view + 8.0 padding on each side 
     scrollView.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 8.0).isActive = true 
     scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8.0).isActive = true 
     scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -8.0).isActive = true 

     // set the height constraint on the scrollView to 0.5 * the main view height 
     scrollView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true 

     // set top, left, right AND bottom constraints on label to 
     // scrollView + 8.0 padding on each side 
     label.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 8.0).isActive = true 
     label.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 8.0).isActive = true 
     label.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -8.0).isActive = true 
     label.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -8.0).isActive = true 

     // set the width of the label to the width of the scrollView (-16 for 8.0 padding on each side) 
     label.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -16.0).isActive = true 

     // configure label: Zero lines + Word Wrapping 
     label.numberOfLines = 0 
     label.lineBreakMode = NSLineBreakMode.byWordWrapping 
     label.font = UIFont.systemFont(ofSize: 17.0) 

     // set the text of the label 
     label.text = s1 

     // ok, we're done... but let's add a button to change the label text, so we 
     // can "see the magic" happening 
     let b = UIButton(type: UIButtonType.system) 
     b.translatesAutoresizingMaskIntoConstraints = false 
     self.view.addSubview(b) 
     b.setTitle("Add a Line", for: .normal) 
     b.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 24.0).isActive = true 
     b.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true 
     b.addTarget(self, action: #selector(self.btnTap(_:)), for: .touchUpInside) 

    } 

    func btnTap(_ sender: Any) { 
     if let t = label.text { 
      counter += 1 
      label.text = t + "\n\n\(counter). Another line" 
     } 
    } 

} 
+0

merci de m'avoir complètement éclairé sur la bonne utilisation et la magie des scrollviews. cet exemple était parfait –

+0

encore une question ... est-il nécessaire de définir 'widthAnchor' pour le label puisque vous avez déjà défini ses ancres de début et de fin? –

+0

Oui ... les ancres de début, de fin, de haut et de bas disent au scrollView "ce sont les bords de votre contentSize" ... l'ancre de largeur indique à l'étiquette "ne vous faites pas plus large que ceci" ... non hauteur, car vous voulez que l'étiquette s'étire aussi haut que nécessaire pour s'adapter au texte. – DonMag

0

donne la contrainte supérieure, gauche, droite et inférieure pour étiqueter avec containerView. et set label.numberOfLines = 0 assurez-vous également que vous avez donné une contrainte supérieure, gauche, droite et inférieure à containerView. cela résoudra votre problème