Swift-comment créer une vue personnalisée pour headerinsection, en utilisant un fichier XIB?

je peux créer une vue personnalisée simple pour l'isolation de la tête dans programmatically comme ci-dessous. Mais je veux faire des choses beaucoup plus complexes peut-être connexion avec une classe différente et atteindre leurs propriétés comme une cellule de tableView. Simplement, je veux voir ce que je fais.

func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

    if(section == 0) {

        let view = UIView() // The width will be the same as the cell, and the height should be set in tableView:heightForRowAtIndexPath:
        let label = UILabel()
        let button   = UIButton(type: UIButtonType.System)

        label.text="My Details"
        button.setTitle("Test Title", forState: .Normal)
        // button.addTarget(self, action: Selector("visibleRow:"), forControlEvents:.TouchUpInside)

        view.addSubview(label)
        view.addSubview(button)

        label.translatesAutoresizingMaskIntoConstraints = false
        button.translatesAutoresizingMaskIntoConstraints = false

        let views = ["label": label, "button": button, "view": view]

        let horizontallayoutContraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[label]-60-[button]-10-|", options: .AlignAllCenterY, metrics: nil, views: views)
        view.addConstraints(horizontallayoutContraints)

        let verticalLayoutContraint = NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0)
        view.addConstraint(verticalLayoutContraint)

        return view
    }

    return nil
}


func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 50
}

y a-t-il quelqu'un pour expliquer comment créer une vue d'en-tête tableView personnalisée en utilisant xib? J'ai rencontré avec de vieux sujets Obj-C mais je suis nouveau avec le langage Swift. Si quelqu'un explique comme détaillé, Il serait grand.

1.question: bouton @IBAction ne se connecte pas avec mon ViewController. (fixe)

résolu avec le propriétaire du fichier, Classe de base ViewController (cliquez sur le menu de contour à gauche.)

2.question: problème de hauteur de L'en-tête (fixe)

Résolu en ajoutant headerView.clipsToBounds = true in view forheaderinsection : method.

Pour contrainte avertissements cette réponse a résolu mes problèmes :

quand J'ai ajouté ImageView même même contrainte de hauteur avec cette méthode dans viewController, il s'écoule sur les lignes de tableView look like picture .

 func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 120
}

si j'utilise, automaticallyAdjustsScrollViewInsets en viewDidLoad, dans ce cas l'image s'écoule sous navigationBar. -fixe

self.automaticallyAdjustsScrollViewInsets = false

3.question: si bouton sous la vue (fixe)

@IBAction func didTapButton(sender: AnyObject) {
    print("tapped")

    if let upView = sender.superview {
        if let headerView = upView?.superview as? CustomHeader {
            print("in section (headerView.sectionNumber)")
        }

    }
}
23
demandé sur Community 2016-04-29 01:33:09

2 réponses

le processus typique pour les en-têtes basés sur NIB serait:

  1. créer UITableViewHeaderFooterView sous-classe avec, au minimum, une sortie pour votre étiquette. Vous pouvez également lui donner un identifiant par lequel vous pouvez rétrograder la section à laquelle cet en-tête correspond. De même, vous pouvez vouloir spécifier un protocole par lequel l'en-tête peut informer le contrôleur de vue des événements (comme le tapotement du bouton). Ainsi, dans Swift 3:

    // if you want your header to be able to inform view controller of key events, create protocol
    
    protocol CustomHeaderDelegate: class {
        func didTapButton(in section: Int)
    }
    
    // define CustomHeader class with necessary `delegate`, `@IBOutlet` and `@IBAction`:
    
    class CustomHeader: UITableViewHeaderFooterView {
        weak var delegate: CustomHeaderDelegate?
    
        @IBOutlet weak var customLabel: UILabel!
    
        var sectionNumber: Int!  // you don't have to do this, but it can be useful to have reference back to the section number so that when you tap on a button, you know which section you came from
    
        @IBAction func didTapButton(_ sender: AnyObject) {
            delegate?.didTapButton(in: sectionNumber)
        }
    
    }
    
  2. Créer de la PLUME. Personnellement, je donne au NIB le même nom que la classe de base pour simplifier la gestion de mes fichiers dans mon projet et éviter la confusion. Quoi qu'il en soit, les étapes clés comprennent:

    • créer la vue NIB, ou si vous avez commencé avec une NIB vide, ajouter la vue à la NIB;

    • définir la classe de base de la vue pour être quel que soit votre UITableViewHeaderFooterView sous-classe était (en mon exemple, CustomHeader );

    • ajouter vos contrôles et contraintes dans IB;

    • raccordement @IBOutlet références aux points de vente dans votre code Swift;

    • accrochez le bouton au @IBAction

  3. Dans le viewDidLoad dans la vue du contrôleur, le registre de la PLUME. Dans Swift 3:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: "CustomHeader")
    }
    

    ou dans Swift 2:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        tableView.registerNib(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: "CustomHeader")
    }
    
  4. Dans viewForHeaderInSection , dequeue réutilisables vue en utilisant le même identifiant que vous avez spécifié à l'étape précédente. Après avoir fait cela, vous pouvez maintenant utiliser votre sortie, vous n'avez rien à faire avec les contraintes créées par programmation, etc. Le seul que vous devez faire (pour que le protocole fonctionne) est de spécifier son délégué. Par exemple, dans Swift 3:

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomHeader") as! CustomHeader
    
        headerView.customLabel.text = content[section].name  // set this however is appropriate for your app's model
        headerView.sectionNumber = section
        headerView.delegate = self
    
        return headerView
    }
    
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 44  // or whatever
    }
    

    ou, dans Swift 2:

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = tableView.dequeueReusableHeaderFooterViewWithIdentifier("CustomHeader") as! CustomHeader
    
        headerView.customLabel.text = content[section].name
        headerView.sectionNumber = section
        headerView.delegate = self
    
        return headerView
    }
    
    override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 44  // or whatever
    }
    
  5. évidemment, si vous voulez spécifier le contrôleur de vue comme le delegate pour le bouton dans la vue d'en-tête, vous devez vous conformer à ce protocole:

    extension ViewController: CustomHeaderDelegate {
        func didTapButton(in section: Int) {
            print("\(section)")
        }
    }
    

tout cela semble déroutant quand j'énumère toutes les étapes impliquées, mais c'est vraiment très simple une fois que vous l'avez fait une ou deux fois. Je pense que c'est plus simple que de construire la vue d'en-tête par programmation.

61
répondu Rob 2017-01-30 08:30:32

la réponse de Rob, bien qu'elle semble convaincante et qu'elle ait résisté à l'épreuve du temps, est fausse et l'a toujours été. Il est difficile de se tenir seul face à la foule écrasante "sagesse" de l'acceptation et de nombreuses voix positives, mais je vais essayer de convoquer le courage de dire la vérité.

le problème, tout simplement, est que vous ne pouvez pas transformer magiquement un uivi dans un nib en un Uitable Viewheaderfooterview simplement en le déclarant dans L'Inspecteur D'identité. Un Uitable viewheaderfooterview a des caractéristiques importantes qui sont la clé de son fonctionnement correct, et un UIView simple, peu importe comment vous pouvez cast il, manque.

  • A Uitable Viewheaderfooterview has a contentView , et toutes vos sous-vues personnalisées doivent être ajoutées à cela, pas à la vue Uitable Viewheaderfooterview.

    mais un uivi mystérieusement Uitable viewheaderfooterview ne contient pas ce contentView dans le nib. Ainsi, lorsque Rob dit "ajouter vos commandes et vos contraintes en IB", il vous demande d'ajouter les fichiers directement à la vue de navigation Utilisable , et Non à sa contentView . L'en-tête ainsi se termine pas correctement configuré.

  • un autre signe de la question Est que vous n'êtes pas autorisé à donner un Viewheaderfooterview Uitable couleur d'arrière-plan. Si vous le faites, vous obtiendrez ce message dans la console:

    définir la couleur de fond sur Uitable Viewheaderfooterview a été déprécié. S'il vous plaît définir un uivi personnalisé avec votre couleur d'arrière-plan désirée à la propriété backgroundView à la place.

    mais dans le nib, vous ne pouvez pas aide définir une couleur de fond sur votre Visionnewheaderfooterview utilisable, et vous faire obtenez ce message dans la console.

alors quelle est la bonne réponse à la question? Il y a pas possible réponse. Apple a fait une énorme gaffe ici. Ils ont fourni une méthode qui vous permet d'enregistrer un nib comme source de votre vue Uitablewheaderfooterview, mais il y a aucune vue Uitablewheaderfooterview dans la bibliothèque D'objets . Par conséquent, cette méthode est inutile . Il est impossible de concevoir un Viewheaderfooterview Uitable correctement dans un nib.

c'est un énorme bug dans Xcode. J'ai déposé un rapport de bogue sur cette affaire en 2013 et il est toujours là, ouvert. Je refile le bug année après année, et Apple continue de repousser, en disant "Il n'a pas été déterminé comment ou quand le problème sera résolu."Donc, ils reconnaissent le bug, mais ils ne font rien à ce sujet.

ce que vous pouvez faire, cependant, est de concevoir un uivi normal dans le nib, puis, dans le code (dans votre mise en œuvre de viewForHeaderInSection ), chargez la vue manuellement à partir du nib et le bourrer dans le contentView de votre vue en-tête.

par exemple, disons que nous voulons concevoir notre en-tête dans la nib, et nous avons une étiquette dans l'en-tête à laquelle nous voulons connecter une sortie lab . Ensuite, nous avons besoin d'un en-tête personnalisé de classe et personnalisé voir la classe:

class MyHeaderView : UITableViewHeaderFooterView {
    weak var content : MyHeaderViewContent!
}
class MyHeaderViewContent : UIView {
    @IBOutlet weak var lab : UILabel!
}

nous enregistrons notre vue en-tête classe , pas le nib:

self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)

Dans la vue xib fichier, nous déclarons notre point de vue à être un MyHeaderViewContent - pas un MyHeaderView.

dans viewForHeaderInSection , nous arrachons la vue de la nib, la remplissons dans le contentView de l'en-tête, et configurons La référence à elle:

override func tableView(_ tableView: UITableView, 
    viewForHeaderInSection section: Int) -> UIView? {
    let h = tableView.dequeueReusableHeaderFooterView(
        withIdentifier: self.headerID) as! MyHeaderView
    if h.content == nil {
        let v = UINib(nibName: "MyHeaderView", bundle: nil).instantiate
            (withOwner: nil, options: nil)[0] as! MyHeaderViewContent
        h.contentView.addSubview(v)
        v.translatesAutoresizingMaskIntoConstraints = false
        v.topAnchor.constraint(equalTo: h.contentView.topAnchor).isActive = true
        v.bottomAnchor.constraint(equalTo: h.contentView.bottomAnchor).isActive = true
        v.leadingAnchor.constraint(equalTo: h.contentView.leadingAnchor).isActive = true
        v.trailingAnchor.constraint(equalTo: h.contentView.trailingAnchor).isActive = true
        h.content = v
        // other initializations for all headers go here
    }
    h.content.lab.text = // whatever
    // other initializations for this header go here
    return h
}

C'est terrible et ennuyeux, mais c'est le meilleur que vous pouvez faire.

14
répondu matt 2018-04-29 13:31:18