99 votes

Façon correcte de charger une Nib pour une sous-classe UIView

Je suis conscient que cette question a déjà été posée, mais les réponses se contredisent et je ne sais plus où j'en suis, alors je vous prie de ne pas m'enflammer.

Je veux avoir une carte réutilisable UIView dans toute mon application. Je veux décrire l'interface en utilisant un fichier nib.

Disons maintenant qu'il s'agit d'une vue d'indicateur de chargement avec un indicateur d'activité. Je voudrais qu'un événement instancie cette vue et l'anime dans la vue d'un contrôleur de vue. Je pourrais décrire l'interface de la vue sans problème de manière programmatique, en créant les éléments de manière programmatique et en définissant leur cadre dans une méthode init, etc.

Mais comment faire en utilisant une plume ? En conservant la taille donnée dans le constructeur d'interface sans avoir à définir un cadre.

J'ai réussi à faire comme ça, mais je suis sûr que c'est faux (c'est juste une vue avec un sélecteur dedans) :

 - (id)initWithDataSource:(NSDictionary *)dataSource {
        self = [super init];
        if (self){
            self = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil] objectAtIndex:0];
            self.pickerViewData = dataSource;
            [self configurePickerView];
        }
        return self;
    }

Mais j'écrase le self, et quand je l'instancie :

FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary];
    selectView.delegate = self;

    selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]);

Je dois définir manuellement le cadre plutôt que de le faire récupérer par IB.

EDIT : Je veux créer cette vue personnalisée dans un contrôleur de vue, et avoir accès au contrôle des éléments de la vue. Je ne veux pas d'un nouveau contrôleur de vue.

Merci

EDIT : Je ne sais pas si c'est la meilleure pratique, je suis sûr que ce n'est pas le cas, mais c'est comme ça que je l'ai fait :

FSASelectView *selectView = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@",[FSASelectView class]] owner:self options:nil] objectAtIndex:0];
    selectView.delegate = self;
    [selectView configurePickerViewWithData:ds];
    selectView.frame = CGRectMake(0, self.view.bottom + 50, selectView.width, selectView.height);
    selectView.alpha = 0.9;
    [self.view addSubview:selectView];
    [UIView animateWithDuration: 0.25 delay: 0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{
                            selectView.frame = CGRectMake(0, self.view.bottom - selectView.height, selectView.width, selectView.height);
                            selectView.alpha = 1;
                        } completion:^(BOOL finished) {
                        }];

Une pratique correcte toujours souhaitée

Aurait-il fallu utiliser un contrôleur de vue et init avec le nom de la nib ? Aurais-je dû définir la nib dans une méthode d'initialisation de UIView dans le code ? Ou est-ce que ce que j'ai fait est correct ?

132voto

Premsuraj Points 950
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]

Je l'utilise pour initialiser les vues personnalisées réutilisables que j'ai.


Notez que vous pouvez utiliser "firstObject" à la fin, c'est un peu plus propre. "firstObject" est une méthode pratique pour NSArray et NSMutableArray.

Voici un exemple typique de chargement d'un xib à utiliser comme en-tête de tableau. Dans votre fichier YourClass.m

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject;
}

Normalement, dans le TopArea.xib vous devez cliquer sur Propriétaire du fichier et définir le propriétaire du fichier à YourClass . Alors en fait dans YourClass.h vous auriez des propriétés IBOutlet. Sur TopArea.xib vous pouvez faire glisser les contrôles vers ces points de vente.

N'oubliez pas qu'en TopArea.xib vous devrez peut-être cliquez sur la vue elle-même et faites-le glisser vers une sortie, pour en avoir le contrôle, si nécessaire. (Un conseil très utile est que lorsque vous faites cela pour les lignes de cellules de tableau, vous devez absolument le faire - vous devez connecter la vue elle-même à la propriété pertinente dans votre code).

39voto

Ans Points 3101

Si vous voulez garder votre CustomView et son xib indépendant de File's Owner puis suivez les étapes suivantes

  • Laissez le File's Owner champ vide.
  • Cliquez sur la vue réelle dans xib de votre CustomView et définir son Custom Class comme CustomView (nom de votre classe de vue personnalisée)
  • Ajouter IBOutlet en .h de votre vue personnalisée.
  • Sur .xib de votre vue personnalisée, cliquez sur vue et allez dans Connection Inspector . Ici, vous trouverez tous vos IBOutlets que vous avez définis dans la section .h fichier
  • Reliez-les à leur vue respective.

sur .m de votre CustomView remplacez la classe init La méthode est la suivante

-(CustomView *) init{
    CustomView *result = nil;
    NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil];
    for (id anObject in elements)
    {
        if ([anObject isKindOfClass:[self class]])
        {
            result = anObject;
            break;
        }
    }
    return result;
}

Maintenant, quand vous voulez charger votre CustomView utilisez la ligne de code suivante [[CustomView alloc] init];

25voto

iphonic Points 4580

Suivez les étapes suivantes

  1. Créez une classe nommée MyView .h/.m de type UIView .

  2. Créer un xib du même nom MyView.xib .

  3. Maintenant, changez la classe du propriétaire du fichier en UIViewController de NSObject dans xib. Voir l'image ci-dessous enter image description here

  4. Connectez la vue du propriétaire du fichier à votre vue. Voir l'image ci-dessous enter image description here

  5. Changez la classe de votre vue en MyView . Identique à 3.

  6. Les contrôles placés créent des IBOutlets.

Voici le code pour charger la vue :

UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];

J'espère que cela vous aidera.

Clarification :

UIViewController est utilisé pour charger votre xib et la vue que le UIViewController a est en fait MyView que vous avez attribué dans le xib de MyView .

Démo J'ai fait une capture de démonstration aquí

11voto

Adam Waite Points 2242

Je réponds à ma propre question environ 2 ans plus tard, mais...

Il utilise une extension de protocole, ce qui permet de le faire sans code supplémentaire pour toutes les classes.

/*

Prerequisites
-------------
- In IB set the view's class to the type hook up any IBOutlets
- In IB ensure the file's owner is blank

*/

public protocol CreatedFromNib {
    static func createFromNib() -> Self?
    static func nibName() -> String?
}

extension UIView: CreatedFromNib { }

public extension CreatedFromNib where Self: UIView {

    public static func createFromNib() -> Self? {
        guard let nibName = nibName() else { return nil }
        guard let view = NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil).last as? Self else { return nil }
        return view
    }

    public static func nibName() -> String? {
        guard let n = NSStringFromClass(Self.self).componentsSeparatedByString(".").last else { return nil }
        return n
    }
}

// Usage:
let myView = MyView().createFromNib()

8voto

vkalit Points 647

Dans Swift :

Par exemple, le nom de votre classe personnalisée est InfoView

Au début, vous créez des fichiers InfoView.xib y InfoView.swift comme ça :

import Foundation
import UIKit

class InfoView: UIView {
    class func instanceFromNib() -> UIView {
    return UINib(nibName: "InfoView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
}

Ensuite, fixez File's Owner a UIViewController comme ça :

enter image description here

Renommez votre View a InfoView :

enter image description here

Cliquez avec le bouton droit de la souris pour File's Owner et de connecter votre view avec votre InfoView :

enter image description here

Assurez-vous que le nom de la classe est InfoView :

enter image description here

Et après cela, vous pouvez ajouter l'action au bouton dans votre classe personnalisée sans aucun problème :

enter image description here

Et l'utilisation de cette classe personnalisée dans votre MainViewController :

func someMethod() {
    var v = InfoView.instanceFromNib()
    v.frame = self.view.bounds
    self.view.addSubview(v)
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X