wake-up-neo.com

Richtige Methode zum Laden einer Nib für eine UIView-Unterklasse

Mir ist bewusst, dass diese Frage bereits gestellt wurde, aber die Antworten sind widersprüchlich und ich bin verwirrt.

Ich möchte eine wiederverwendbare UIView Unterklasse in meiner App haben. Ich möchte die Schnittstelle mit einer NIB-Datei beschreiben.

Angenommen, es handelt sich um eine Ladeanzeigeanzeige mit einer Aktivitätsanzeige. Ich möchte auf jeden Fall diese Ansicht instanziieren und in die Ansicht eines Ansichtscontrollers animieren. Ich konnte die Benutzeroberfläche der Ansicht problemlos programmgesteuert beschreiben, die Elemente programmgesteuert erstellen und ihren Frame innerhalb einer Init-Methode usw. festlegen.

Wie kann ich das mit einer Feder machen? Beibehaltung der im Interface Builder angegebenen Größe, ohne dass ein Frame festgelegt werden muss.

Ich habe es geschafft, aber ich bin sicher, es ist falsch (es ist nur eine Ansicht mit einem Picker darin):

 - (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;
    }

Aber ich überschreibe mich selbst und wenn ich es instanziiere:

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

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

Ich muss den Rahmen manuell einstellen, anstatt ihn von IB abholen zu lassen.

BEARBEITEN: Ich möchte diese benutzerdefinierte Ansicht in einem Ansichtscontroller erstellen und kann die Elemente der Ansicht steuern. Ich möchte keinen neuen View Controller.

Vielen Dank

EDIT: Ich weiß nicht, ob dies die beste Vorgehensweise ist, ich bin sicher, dass es nicht so ist, aber so habe ich es gemacht:

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) {
                        }];

Richtiges Üben noch gewünscht

Sollte dies mit einem View-Controller und Init mit dem Namen der Schreibfeder geschehen sein? Sollte ich die Spitze in einer UIView-Initialisierungsmethode im Code festgelegt haben? Oder ist das, was ich getan habe, in Ordnung?

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

Ich benutze dies, um die wiederverwendbaren benutzerdefinierten Ansichten zu initialisieren, die ich habe.


Beachten Sie, dass Sie "firstObject" am Ende dort verwenden können, es ist ein wenig sauberer. "firstObject" ist eine praktische Methode für NSArray und NSMutableArray.

Hier ist ein typisches Beispiel für das Laden einer xib, die als Tabellenüberschrift verwendet werden soll. In Ihrer Datei YourClass.m

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

Normalerweise wird im TopArea.xib, klicken Sie auf Dateieigentümer und setzen Sie den Dateieigentümer auf YourClass. Dann hätten Sie tatsächlich in YourClass.h IBOutlet-Eigenschaften. Im TopArea.xib können Sie Steuerelemente auf diese Ausgänge ziehen.

Vergiss das nicht in TopArea.xib müssen Sie möglicherweise klicken Sie auf die Ansicht selbst und ziehen Sie diese auf eine Steckdose, damit Sie sie bei Bedarf steuern können. (Ein sehr lohnender Tipp ist, dass Sie dies unbedingt tun müssen, wenn Sie dies für Tabellenzellenzeilen tun - Sie müssen die Ansicht selbst mit der entsprechenden Eigenschaft in Ihrem Code verbinden.)

129
Premsuraj

Wenn Sie Ihr CustomView und sein xib unabhängig von File's Owner Halten möchten, gehen Sie folgendermaßen vor

  • Lassen Sie das Feld File's Owner Leer.
  • Klicken Sie auf die aktuelle Ansicht in der xib Datei Ihres CustomView und setzen Sie den Custom Class Als CustomView (Name Ihrer benutzerdefinierten Ansichtsklasse)
  • Fügen Sie IBOutlet in die .h - Datei Ihrer benutzerdefinierten Ansicht ein.
  • Klicken Sie in der .xib - Datei Ihrer benutzerdefinierten Ansicht auf Ansicht und gehen Sie zu Connection Inspector. Hier finden Sie alle Ihre IBOutlets, die Sie in der Datei .h Definieren
  • Verbinden Sie sie mit ihrer jeweiligen Ansicht.

überschreiben Sie in der Datei .m Ihrer Klasse CustomView die Methode init wie folgt

-(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;
}

Wenn Sie nun Ihr CustomView laden möchten, verwenden Sie die folgende Codezeile [[CustomView alloc] init];

39
Ans

Befolgen Sie die folgenden Schritte

  1. Erstellen Sie eine Klasse mit dem Namen MyView .h/.m vom Typ UIView.
  2. Erstelle eine xib mit dem gleichen Namen MyView.xib.
  3. Ändern Sie jetzt die Klasse File Owner in UIViewController von NSObject in xib. Siehe das Bild unten enter image description here
  4. Verbinden Sie die Dateieigentümeransicht mit Ihrer Ansicht. Siehe das Bild unten enter image description here

  5. Ändern Sie die Klasse Ihrer Ansicht in MyView. Gleich wie 3.

  6. Place Controls erstellen IBOutlets.

Hier ist der Code zum Laden der Ansicht:

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

Ich hoffe es hilft.

Erläuterung :

UIViewController wird verwendet, um Ihre xib zu laden, und die Ansicht, die die UIViewController hat, ist tatsächlich die MyView, die Sie in der MyView-xib zugewiesen haben.

Demo Ich habe eine Demo aufgenommen hier

24
iphonic

Beantworte meine eigene Frage nach 2 oder etwas später hier, aber ...

Es wird eine Protokollerweiterung verwendet, sodass Sie für alle Klassen auf zusätzlichen Code verzichten können.

/*

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

In Swift:

Der Name Ihrer benutzerdefinierten Klasse lautet beispielsweise InfoView

Zuerst erstellen Sie Dateien InfoView.xib und InfoView.Swiftso was:

import Foundation
import UIKit

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

Dann setze File's Owner bis UIViewController wie folgt:

enter image description here

Benenne dein View in InfoView um:

enter image description here

Rechtsklick auf File's Owner und verbinde dein view Feld mit deinem InfoView:

enter image description here

Stellen Sie sicher, dass der Klassenname InfoView ist:

enter image description here

Danach können Sie die Aktion problemlos zu button in Ihrer benutzerdefinierten Klasse hinzufügen:

enter image description here

Und Verwendung dieser benutzerdefinierten Klasse in Ihrem MainViewController:

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

Nun, Sie können entweder die xib mit einem View-Controller initialisieren und viewController.view verwenden. oder mach es so wie du es getan hast. Nur eine UIView -Unterklasse als Controller für UIView zu definieren, ist eine schlechte Idee.

Wenn Sie keine Ausgänge in Ihrer benutzerdefinierten Ansicht haben, können Sie diese direkt mit einer UIViewController -Klasse initialisieren.

pdate: In Ihrem Fall:

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

Andernfalls müssen Sie ein benutzerdefiniertes UIViewController erstellen (um es als Eigentümer der Datei festzulegen, damit die Ausgänge ordnungsgemäß verbunden sind).

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

Wo immer Sie die Ansicht hinzufügen möchten, können Sie sie verwenden.

[parentView addSubview:yCustCon.view];

Das Übergeben eines anderen Ansichtscontrollers (der bereits für eine andere Ansicht verwendet wird) als Eigentümer beim Laden der xib ist jedoch keine gute Idee, da die Ansichtseigenschaft des Controllers geändert wird und Sie dies nicht tun, wenn Sie auf die ursprüngliche Ansicht zugreifen möchten habe einen Verweis darauf.

EDIT: Dieses Problem tritt auf, wenn Sie Ihre neue xib mit dem Eigentümer der Datei als dieselbe Hauptklasse UIViewController eingerichtet und die view -Eigenschaft an die neue xib-Ansicht gebunden haben.

d.h.

  • YourMainViewController - verwaltet - mainView
  • CustomView - muss bei Bedarf von xib geladen werden.

Der folgende Code wird später Verwirrung stiften, wenn Sie ihn in view geschrieben haben und YourMainViewController geladen haben. Das ist, weil self.view bezieht sich ab diesem Zeitpunkt auf Ihre benutzerdefinierte Ansicht

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