web-dev-qa-db-fra.com

Définition dynamique de la hauteur de tableHeaderView

Mon application crée un UITableViewController contenant un tableHeaderView personnalisé pouvant avoir une hauteur arbitraire. Je me suis efforcé de trouver un moyen de définir cet en-tête de manière dynamique, car il semble que les moyens suggérés aient été raccourcis. Mon code pertinent UITableViewController: 

import UIKit
import SafariServices

class RedditPostViewController: UITableViewController, NetworkCommunication, SubViewLaunchLinkManager {

    //MARK: UITableViewDataSource
    var post: PostData?
    var tree: CommentTree?
    weak var session: Session! = Session.sharedInstance

    override func viewDidLoad() {
        super.viewDidLoad()

        // Get post info from api
        guard let postData = post else { return }

        //Configure comment table
        self.tableView.registerClass(RedditPostCommentTableViewCell.self, forCellReuseIdentifier: "CommentCell")

       let tableHeader = PostView(withPost: postData, inViewController: self)
       let size = tableHeader.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize)
       let height = size.height
       let width = size.width
       tableHeader.frame = CGRectMake(0, 0, width, height)
       self.tableView.tableHeaderView = tableHeader


       session.getRedditPost(postData) { (post) in
           self.post = post?.post
           self.tree = post?.comments
           self.tableView.reloadData()
       }
    }
}

Cela se traduit par la mise en page incorrecte suivante: 

Si je change la ligne: tableHeader.frame = CGRectMake(0, 0, width, height) en tableHeader.frame = CGRectMake(0, 0, width, 1000), la tableHeaderView se présentera correctement: 

Je ne suis pas sûr de ce que je fais mal ici. Aussi, classe UIView personnalisée, si cela aide: 

import UIKit
import Foundation

protocol SubViewLaunchLinkManager: class {
    func launchLink(sender: UIButton)
}

class PostView: UIView {

    var body: UILabel?
    var post: PostData?
    var domain: UILabel?
    var author: UILabel?
    var selfText: UILabel?
    var numComments: UILabel?

    required init?(coder aDecoder: NSCoder) {
        fatalError("Not implemented yet")
    }

    init(withPost post: PostData, inViewController viewController: SubViewLaunchLinkManager) {
        super.init(frame: CGRectZero)

        self.post = post
        self.backgroundColor = UIColor.lightGrayColor()

        let launchLink = UIButton()
        launchLink.setImage(UIImage(named: "circle-user-7"), forState: .Normal)
        launchLink.addTarget(viewController, action: "launchLink:", forControlEvents: .TouchUpInside)
        self.addSubview(launchLink)

        selfText = UILabel()
        selfText?.backgroundColor = UIColor.whiteColor()
        selfText?.numberOfLines = 0
        selfText?.lineBreakMode = .ByWordWrapping
        selfText!.text = post.selfText
        self.addSubview(selfText!)
        selfText?.sizeToFit()

        //let attributedString = NSAttributedString(string: "Test"/*post.selfTextHtml*/, attributes: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType])
        //selfText.attributedText = attributedString

        body = UILabel()
        body!.text = post.title
        body!.numberOfLines = 0
        body!.lineBreakMode = .ByWordWrapping
        body!.textAlignment = .Justified
        self.addSubview(body!)

        domain = UILabel()
        domain!.text = post.domain
        self.addSubview(domain!)

        author = UILabel()
        author!.text = post.author
        self.addSubview(author!)

        numComments = UILabel()
        numComments!.text = "\(post.numComments)"
        self.addSubview(numComments!)

        body!.translatesAutoresizingMaskIntoConstraints = false
        domain!.translatesAutoresizingMaskIntoConstraints = false
        author!.translatesAutoresizingMaskIntoConstraints = false
        selfText!.translatesAutoresizingMaskIntoConstraints = false
        launchLink.translatesAutoresizingMaskIntoConstraints = false
        numComments!.translatesAutoresizingMaskIntoConstraints = false

        let views: [String: UIView] = ["body": body!, "domain": domain!, "author": author!, "numComments": numComments!, "launchLink": launchLink, "selfText": selfText!]
        //let selfTextSize = selfText?.sizeThatFits((selfText?.frame.size)!)
        //print(selfTextSize)
        //let metrics = ["selfTextHeight": selfTextSize!.height]

                   self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[body]-[selfText]-[domain]-|", options: [], metrics: nil, views: views))
       self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[body]-[selfText]-[author]-|", options: [], metrics: nil, views: views))
    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[body]-[selfText]-[numComments]-|", options: [], metrics: nil, views: views))
    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[launchLink]-[numComments]-|", options: [], metrics: nil, views: views))
    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[body][launchLink]|", options: [], metrics: nil, views: views))
    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[selfText][launchLink]|", options: [], metrics: nil, views: views))
    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[domain][author][numComments][launchLink]|", options: [], metrics: nil, views: views))
}

override func layoutSubviews() {
    super.layoutSubviews()
    body?.preferredMaxLayoutWidth = body!.bounds.width
}
}
17
TravMatth

Copié de cet article . (Assurez-vous de le voir si vous recherchez plus de détails)

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let headerView = tableView.tableHeaderView {

        let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        var headerFrame = headerView.frame

        //Comparison necessary to avoid infinite loop
        if height != headerFrame.size.height {
            headerFrame.size.height = height
            headerView.frame = headerFrame
            tableView.tableHeaderView = headerView
        }
    }
}
60
TravMatth

Version plus condensée de la réponse d'OP, avec l'avantage de permettre une mise en page naturelle (notez que cette solution utilise view Will LayoutSubviews):

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    if let header = tableView.tableHeaderView {
        let newSize = header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        header.frame.size.height = newSize.height
    }
}

Comme cela est indiqué, il n’ya aucune excuse pour Apple de ne pas prendre en charge la hauteur automatique d’en-tête/pied de page de la vue tableau comme ils le font maintenant avec des cellules. Frustrant.

Merci à TravMatth pour la réponse originale.

6
Nathan Hosselton

Détermination de la taille du cadre de l'en-tête à l'aide de 

header.systemLayoutSizeFitting(UILayoutFittingCompressedSize)

comme suggéré dans les réponses ci-dessus ne fonctionnait pas pour moi lorsque ma vue en-tête consistait en une seule étiquette multiligne. Lorsque le mode de saut de ligne de l'étiquette est défini sur wrap, le texte est simplement coupé: 

 enter image description here

Au lieu de cela, ce qui a fonctionné pour moi a été d'utiliser la largeur de la vue tableau et une hauteur de 0 comme taille cible:

header.systemLayoutSizeFitting(CGSize(width: tableView.bounds.width, height: 0))

 enter image description here

Mettre tout cela ensemble (je préfère utiliser une extension):

extension UITableView {
    func updateHeaderViewHeight() {
        if let header = self.tableHeaderView {
            let newSize = header.systemLayoutSizeFitting(CGSize(width: self.bounds.width, height: 0))
            header.frame.size.height = newSize.height
        }
    }
}

Et appelez comme ça:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    tableView.updateHeaderViewHeight()
}
5
NSExceptional

La simple implémentation de ces deux méthodes de délégué UITableView a fonctionné pour moi:

-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section
{
    return 100;
}

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return UITableViewAutomaticDimension;
}
1
kachulach

Si vous rencontrez toujours des problèmes de présentation avec l'exemple de code ci-dessus, il est possible que vous désactiviez la variable translatesAutoresizingMaskIntoConstraints dans l'affichage en-tête personnalisé. Dans ce cas, vous devez redéfinir translatesAutoresizingMaskIntoConstraints sur true après avoir défini le cadre de l'en-tête.

Voici l'exemple de code que j'utilise et qui fonctionne correctement sur iOS 11. 

public override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    guard let headerView = tableView.tableHeaderView else { return }

    let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
    var headerFrame = headerView.frame

    if height != headerFrame.size.height {
        headerFrame.size.height = height
        headerView.frame = headerFrame
        tableView.tableHeaderView = headerView

        if #available(iOS 9.0, *) {
            tableView.layoutIfNeeded()
        }
    }

    headerView.translatesAutoresizingMaskIntoConstraints = true
}
0
emrekyv