wake-up-neo.com

Wie erstelle ich eine benutzerdefinierte iOS-Ansichtsklasse und instanziiere mehrere Kopien davon (in IB)?

Ich mache gerade eine App, die mehrere Timer hat, die im Grunde alle gleich sind.

Ich möchte eine benutzerdefinierte Klasse erstellen, die den gesamten Code für die Timer sowie das Layout/die Animationen verwendet, damit ich 5 identische Timer haben kann, die unabhängig voneinander arbeiten.

Ich habe das Layout mit IB (xcode 4.2) erstellt und der gesamte Code für die Timer befindet sich derzeit nur in der Klasse viewcontroller.

Ich habe Schwierigkeiten, mein Gehirn darum zu kümmern, wie man alles in eine benutzerdefinierte Klasse einkapselt und sie dann dem Viewcontroller hinzufügt. Jede Hilfe wäre sehr willkommen.

104

Um konzeptionell zu antworten, sollte Ihr Timer wahrscheinlich eine Unterklasse von UIView anstelle von NSObject sein.

Um eine Instanz Ihres Timers in IB zu instanziieren, ziehen Sie einfach ein UIView auf die Ansicht Ihres View Controllers und setzen Sie die Klasse auf den Klassennamen Ihres Timers.

enter image description here

Denken Sie daran, Ihre Timer-Klasse in Ihrem View-Controller #import Zu speichern.

Bearbeiten: für IB-Design (zur Code-Instanziierung siehe Revisionsverlauf)

Ich bin mit Storyboard überhaupt nicht vertraut, aber ich weiß, dass Sie Ihr Interface in IB mit einer .xib - Datei erstellen können, die fast identisch mit der Storyboard-Version ist. Sie sollten sogar in der Lage sein, Ihre Ansichten als Ganzes von Ihrer vorhandenen Oberfläche in die Datei .xib Zu kopieren und einzufügen.

Um dies zu testen, habe ich ein neues leeres .xib Mit dem Namen "MyCustomTimerView.xib" erstellt. Dann habe ich eine Ansicht hinzugefügt und eine Beschriftung und zwei Schaltflächen hinzugefügt. Wie so:

enter image description here

Ich habe eine neue Objective-C-Klasse mit der Unterklasse UIView mit dem Namen "MyCustomTimer" erstellt. In meinem .xib Habe ich meine Klasse File's Owner auf MyCustomTimer gesetzt. Jetzt kann ich Aktionen und Ausgänge wie jede andere Ansicht/Steuerung verbinden. Die resultierende Datei .h Sieht folgendermaßen aus:

@interface MyCustomTimer : UIView
@property (strong, nonatomic) IBOutlet UILabel *displayLabel;
@property (strong, nonatomic) IBOutlet UIButton *startButton;
@property (strong, nonatomic) IBOutlet UIButton *stopButton;
- (IBAction)startButtonPush:(id)sender;
- (IBAction)stopButtonPush:(id)sender;
@end

Die einzige Hürde, die noch zu überwinden ist, besteht darin, diesen .xib In meiner Unterklasse UIView abzurufen. Durch die Verwendung von .xib Wird das erforderliche Setup drastisch reduziert. Und da Sie Storyboards zum Laden der Timer verwenden, wissen wir, dass -(id)initWithCoder: der einzige Initialisierer ist, der aufgerufen wird. So sieht die Implementierungsdatei aus:

#import "MyCustomTimer.h"
@implementation MyCustomTimer
@synthesize displayLabel;
@synthesize startButton;
@synthesize stopButton;
-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super initWithCoder:aDecoder])){
        [self addSubview:
         [[[NSBundle mainBundle] loadNibNamed:@"MyCustomTimerView" 
                                        owner:self 
                                      options:nil] objectAtIndex:0]];
    }
    return self;
}
- (IBAction)startButtonPush:(id)sender {
    self.displayLabel.backgroundColor = [UIColor greenColor];
}
- (IBAction)stopButtonPush:(id)sender {
    self.displayLabel.backgroundColor = [UIColor redColor];
}
@end

Die Methode mit dem Namen loadNibNamed:owner:options: Macht genau das, wie es sich anhört. Es lädt die Nib und setzt die Eigenschaft "File's Owner" auf self. Wir extrahieren das erste Objekt im Array und das ist die Stammansicht der Nib. Wir fügen die Ansicht als Unteransicht hinzu und Voila ist auf dem Bildschirm.

Offensichtlich ändert dies nur die Hintergrundfarbe des Etiketts, wenn die Tasten gedrückt werden, aber dieses Beispiel sollte Sie auf Ihrem Weg voranbringen.

Anmerkungen basierend auf Kommentaren:

Es ist erwähnenswert, dass Sie bei unendlichen Rekursionsproblemen wahrscheinlich den subtilen Trick dieser Lösung verpasst haben. Es tut nicht das, was du denkst. Die Ansicht, die in das Storyboard eingefügt wird, wird nicht angezeigt, sondern lädt eine andere Ansicht als Unteransicht. Die Ansicht, die geladen wird, ist die in der Schreibfeder definierte Ansicht. Der "Dateieigentümer" in der Schreibfeder ist diese unsichtbare Ansicht. Der coole Teil ist, dass diese unsichtbare Ansicht immer noch eine Objective-C-Klasse ist, die als eine Art Ansichtscontroller für die Ansicht verwendet werden kann, die sie von der Schreibfeder einbringt. Zum Beispiel sind die IBAction -Methoden in der MyCustomTimer -Klasse etwas, das Sie in einem Ansichtscontroller mehr erwarten würden als in einer Ansicht.

Als Randnotiz können einige argumentieren, dass dies MVC bricht und ich stimme etwas zu. Aus meiner Sicht ist es enger mit einem benutzerdefinierten UITableViewCell verwandt, der manchmal auch Teilecontroller sein muss.

Es ist auch erwähnenswert, dass diese Antwort eine sehr spezifische Lösung war; Erstellen Sie eine Schreibfeder, die in derselben Ansicht wie auf einem Storyboard angeordnet mehrfach instanziiert werden kann. Sie können sich beispielsweise leicht sechs dieser Timer auf einem iPad-Bildschirm vorstellen. Wenn Sie nur eine Ansicht für einen Ansichtscontroller angeben müssen, der mehrmals in Ihrer Anwendung verwendet werden soll, ist die von jyavenard für diese Frage bereitgestellte Lösung mit ziemlicher Sicherheit die bessere Lösung für Sie.

139
NJones

Schnelles Beispiel

Aktualisiert für Xcode 10 und Swift 4

Hier ist ein grundlegender Spaziergang durch. Ich habe ursprünglich viel gelernt, indem ich diese Youtube-Videoserie gesehen habe. Später habe ich meine Antwort basierend auf diesem Artikel aktualisiert.

Fügen Sie benutzerdefinierte Ansichtsdateien hinzu

Die folgenden zwei Dateien bilden Ihre benutzerdefinierte Ansicht:

  • .xib-Datei, die das Layout enthält
  • .Swift-Datei als Unterklasse UIView

Die Details zum Hinzufügen finden Sie weiter unten.

Xib-Datei

Fügen Sie Ihrem Projekt eine .xib-Datei hinzu (Datei> Neu> Datei ...> Benutzeroberfläche> Ansicht). Ich rufe meine ReusableCustomView.xib.

Erstellen Sie das Layout, das Ihre benutzerdefinierte Ansicht haben soll. Als Beispiel werde ich ein Layout mit einem UILabel und einem UIButton erstellen. Es ist eine gute Idee, das automatische Layout zu verwenden, damit die Größe der Objekte automatisch geändert wird, unabhängig davon, auf welche Größe Sie sie später einstellen. (Ich habe Freeform für die xib-Größe im Attributes-Inspector verwendet, damit ich die simulierten Metriken anpassen konnte, dies ist jedoch nicht erforderlich.)

enter image description here

Swift-Datei

Fügen Sie Ihrem Projekt eine .Swift-Datei hinzu (File> New> File ...> Source> Swift File). Es ist eine Unterklasse von UIView und ich rufe meine ReusableCustomView.Swift.

import UIKit
class ResuableCustomView: UIView {

}

Machen Sie die Datei Swift zum Eigentümer

Gehen Sie zurück zu Ihrer .xib-Datei und klicken Sie in der Dokumentenübersicht auf "Besitzer der Datei". Schreiben Sie im Identitätsinspektor den Namen Ihrer .Swift-Datei als benutzerdefinierten Klassennamen.

enter image description here

Benutzerdefinierten Ansichtscode hinzufügen

Ersetze das ReusableCustomView.Swift Dateiinhalt mit folgendem Code:

import UIKit

@IBDesignable
class ResuableCustomView: UIView {

    let nibName = "ReusableCustomView"
    var contentView:UIView?

    @IBOutlet weak var label: UILabel!

    @IBAction func buttonTap(_ sender: UIButton) {
        label.text = "Hi"
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    func commonInit() {
        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        self.addSubview(view)
        contentView = view
    }

    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
}

Achten Sie darauf, dass die Schreibweise für den Namen Ihrer .xib-Datei richtig ist.

Schließen Sie die Steckdosen und Aktionen an

Schließen Sie die Ausgänge und Aktionen an, indem Sie die Steuerung vom Etikett und der Schaltfläche im xib-Layout zum benutzerdefinierten Ansichtscode Swift) ziehen.

Verwenden Sie Ihre benutzerdefinierte Ansicht

Ihre benutzerdefinierte Ansicht ist jetzt fertig. Alles, was Sie tun müssen, ist, ein UIView in Ihr Hauptstoryboard einzufügen, wo immer Sie wollen. Setzen Sie den Klassennamen der Ansicht im Identitätsinspektor auf ReusableCustomView.

enter image description here

120
Suragch

Antwort für View Controller, keine Views:

Es gibt eine einfachere Möglichkeit, eine xib von einem Storyboard zu laden. Angenommen, Ihr Controller ist vom Typ MyClassController, der von UIViewController erbt.

Sie fügen ein UIViewController mit IB in Ihr Storyboard ein. Ändern Sie den Klassentyp in MyClassController. Löschen Sie die Ansicht, die automatisch im Storyboard hinzugefügt wurde.

Stellen Sie sicher, dass das XIB, das Sie aufrufen möchten, MyClassController.xib heißt.

Wenn die Klasse während des Ladens des Storyboards instanziiert wird, wird die xib automatisch geladen. Der Grund dafür liegt in der Standardimplementierung von UIViewController, die die mit dem Klassennamen benannte XIB aufruft.

39
jyavenard

Dies ist keine wirkliche Antwort, aber ich denke, es ist hilfreich, diesen Ansatz zu teilen.

Ziel-C

  1. Importieren Sie CustomViewWithXib.h und CustomViewWithXib.m in Ihr Projekt
  2. Erstellen Sie die benutzerdefinierten Ansichtsdateien mit demselben Namen (.h/.m/.xib).
  3. Vererben Sie Ihre benutzerdefinierte Klasse von CustomViewWithXib

Swift

  1. Importieren Sie CustomViewWithXib.Swift in Ihr Projekt
  2. Erstellen Sie die benutzerdefinierten Ansichtsdateien mit demselben Namen (.Swift und .xib).
  3. Vererben Sie Ihre benutzerdefinierte Klasse von CustomViewWithXib

Optional:

  1. Wechseln Sie zu Ihrer xib-Datei, und legen Sie den Eigentümer mit Ihrem benutzerdefinierten Klassennamen fest, wenn Sie einige Elemente verbinden möchten (weitere Informationen finden Sie im Abschnitt Machen Sie die Swift -Datei zum Eigentümer von @Suragch-Antworten).

Es ist alles, jetzt können Sie Ihre benutzerdefinierte Ansicht in Ihr Storyboard einfügen und es wird angezeigt :)

CustomViewWithXib.h :

 #import <UIKit/UIKit.h>

/**
 *  All classes inherit from CustomViewWithXib should have the same xib file name and class name (.h and .m)
 MyCustomView.h
 MyCustomView.m
 MyCustomView.xib
 */

// This allows seeing how your custom views will appear without building and running your app after each change.
IB_DESIGNABLE
@interface CustomViewWithXib : UIView

@end

CustomViewWithXib.m :

#import "CustomViewWithXib.h"

@implementation CustomViewWithXib

#pragma mark - init methods

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // load view frame XIB
        [self commonSetup];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // load view frame XIB
        [self commonSetup];
    }
    return self;
}

#pragma mark - setup view

- (UIView *)loadViewFromNib {
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];

    //  An exception will be thrown if the xib file with this class name not found,
    UIView *view = [[bundle loadNibNamed:NSStringFromClass([self class])  owner:self options:nil] firstObject];
    return view;
}

- (void)commonSetup {
    UIView *nibView = [self loadViewFromNib];
    nibView.frame = self.bounds;
    // the autoresizingMask will be converted to constraints, the frame will match the parent view frame
    nibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    // Adding nibView on the top of our view
    [self addSubview:nibView];
}

@end

CustomViewWithXib.Swift :

import UIKit

@IBDesignable
class CustomViewWithXib: UIView {

    // MARK: init methods
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonSetup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        commonSetup()
    }

    // MARK: setup view 

    private func loadViewFromNib() -> UIView {
        let viewBundle = NSBundle(forClass: self.dynamicType)
        //  An exception will be thrown if the xib file with this class name not found,
        let view = viewBundle.loadNibNamed(String(self.dynamicType), owner: self, options: nil)[0]
        return view as! UIView
    }

    private func commonSetup() {
        let nibView = loadViewFromNib()
        nibView.frame = bounds
        // the autoresizingMask will be converted to constraints, the frame will match the parent view frame
        nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        // Adding nibView on the top of our view
        addSubview(nibView)
    }
}

Sie können einige Beispiele finden hier .

Hoffentlich hilft das.

15
Hamza