web-dev-qa-db-fra.com

Manière correcte de charger un Nib pour une sous-classe UIView

Je suis conscient que cette question a déjà été posée, mais les réponses sont contradictoires et je suis confus, alors s'il vous plaît, ne m'embrassez pas.

Je souhaite avoir une sous-classe réutilisable UIView dans mon application. Je veux décrire l'interface en utilisant un fichier nib.

Maintenant, supposons qu'il s'agisse d'une vue d'indicateur de chargement contenant 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 par programmation, en créant les éléments par programmation et en définissant leur cadre dans une méthode init, etc.

Comment puis-je faire cela en utilisant une plume cependant? Conserver la taille donnée dans le constructeur d'interface sans avoir à définir de cadre.

J'ai réussi à le 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 je m'écrase moi-même 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 récupérer auprès de 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, 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) {
                        }];

Bonne pratique toujours recherchée

Est-ce que cela aurait dû être fait en utilisant un contrôleur de vue et init avec nom de nib? Devrais-je avoir défini la nib dans une méthode d'initialisation UIView dans le code? Ou est ce que j'ai fait ok?

98
Adam Waite
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]

J'utilise ceci 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 table. Dans votre dossier 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 cliquez sur le propriétaire du fichier et définissez le propriétaire du fichier sur YourClass. Alors en fait dans YourClass.h vous auriez des propriétés IBOutlet. Dans TopArea.xib, vous pouvez faire glisser les contrôles vers ces prises.

N'oubliez pas que dans TopArea.xib, vous devrez peut-être cliquez sur la vue elle-même et faites-la glisser vers un point de vente, de sorte que vous en ayez le contrôle, si nécessaire. (Un conseil très intéressant est que lorsque vous effectuez cette opération pour les lignes de cellules de tableau, vous devez absolument le faire - vous devez connecter la vue elle-même à la propriété correspondante dans votre code.)

129
Premsuraj

Si vous voulez garder votre CustomView et ses xib indépendamment de File's Owner, puis suivez ces étapes

  • Quitter le File's Owner champ vide.
  • Cliquez sur la vue actuelle dans le fichier xib de votre CustomView et définissez-la Custom Class comme CustomView (nom de votre classe de vue personnalisée)
  • Ajouter IBOutlet dans .h fichier de votre vue personnalisée.
  • Dans .xib fichier de votre vue personnalisée, cliquez dessus et allez dans Connection Inspector. Vous y trouverez tous vos IBOutlets que vous définissez dans .h fichier
  • Connectez-les avec leur vue respective.

dans .m fichier de votre classe CustomView, remplacez la méthode init comme suit

-(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, lorsque vous voulez charger votre CustomView, utilisez la ligne de code suivante [[CustomView alloc] init];

39
Ans

Suivez les étapes suivantes

  1. Créez une classe nommée MyView .h/.m de type UIView.
  2. Crée un xib du même nom MyView.xib.
  3. Maintenant changez la classe File Owner 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. Placez les contrôles créez 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 ça aide.

Clarification :

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

Démo J'ai créé une capture de démo ici

24
iphonic

Répondant à ma propre question à propos de 2 ans ou quelque chose après, mais ...

Il utilise une extension de protocole afin que vous puissiez le faire sans aucun 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()
9
Adam Waite

En rapide:

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

Au début, vous créez des fichiers InfoView.xib et InfoView.Swiftcomme ç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
}

Puis définissez File's Owner à UIViewController comme ceci:

enter image description here

Renommez votre View en InfoView:

enter image description here

Faites un clic droit sur File's Owner et connectez votre champ view à 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)
}
8
vkalit

Vous pouvez également initialiser le fichier xib à l’aide d’un contrôleur de vue et utiliser viewController.view. ou faites-le comme vous l'avez fait. Créer une sous-classe UIView en tant que contrôleur pour UIView est une mauvaise idée.

Si vous ne disposez d'aucun point de vente dans votre vue personnalisée, vous pouvez directement utiliser une classe UIViewController pour l'initialiser.

Mise à jour: Dans votre cas:

UIViewController *genericViewCon = [[UIViewController alloc] initWithNibName:@"CustomView"];
//Assuming you have a reference for the activity indicator in your custom view class
CustomView *myView = (CustomView *)genericViewCon.view;
[parentView addSubview:myView];
//And when necessary
[myView.activityIndicator startAnimating]; //or stop

Sinon, vous devez créer un UIViewController personnalisé (pour en faire le propriétaire du fichier afin que les sorties soient correctement câblées).

YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].

Où que vous souhaitiez ajouter la vue, vous pouvez l'utiliser.

[parentView addSubview:yCustCon.view];

Cependant, passer le contrôleur d’une autre vue (déjà utilisé pour une autre vue) en tant que propriétaire lors du chargement de la xib n’est pas une bonne idée, car la propriété view du contrôleur sera modifiée et vous ne pourrez pas accéder à la vue originale. avoir une référence à elle.

EDIT: Vous devrez faire face à ce problème si vous avez configuré votre nouvelle xib avec le propriétaire du fichier comme étant la même classe principale UIViewController et lié la propriété view à la nouvelle vue xib.

c'est à dire;

  • YourMainViewController - gère - mainView
  • CustomView - doit être chargé à partir de xib selon les besoins.

Le code ci-dessous causera de la confusion plus tard, si vous l'écrivez dans la vue, le chargement de YourMainViewController. C'est parce que self.view à partir de ce moment-là, on fera référence à votre vue personnalisée

-(void)viewDidLoad:(){
  UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}
2
Rakesh