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?
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.)
Wenn Sie Ihr CustomView
und sein xib
unabhängig von File's Owner
Halten möchten, gehen Sie folgendermaßen vor
File's Owner
Leer.xib
Datei Ihres CustomView
und setzen Sie den Custom Class
Als CustomView
(Name Ihrer benutzerdefinierten Ansichtsklasse)IBOutlet
in die .h
- Datei Ihrer benutzerdefinierten Ansicht ein..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ü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];
Befolgen Sie die folgenden Schritte
UIView
.MyView.xib
.UIViewController
von NSObject
in xib. Siehe das Bild unten Verbinden Sie die Dateieigentümeransicht mit Ihrer Ansicht. Siehe das Bild unten
Ändern Sie die Klasse Ihrer Ansicht in MyView
. Gleich wie 3.
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
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()
In Swift:
Der Name Ihrer benutzerdefinierten Klasse lautet beispielsweise InfoView
Zuerst erstellen Sie Dateien InfoView.xib
und InfoView.Swift
so 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:
Benenne dein View
in InfoView
um:
Rechtsklick auf File's Owner
und verbinde dein view
Feld mit deinem InfoView
:
Stellen Sie sicher, dass der Klassenname InfoView
ist:
Danach können Sie die Aktion problemlos zu button in Ihrer benutzerdefinierten Klasse hinzufügen:
Und Verwendung dieser benutzerdefinierten Klasse in Ihrem MainViewController
:
func someMethod() {
var v = InfoView.instanceFromNib()
v.frame = self.view.bounds
self.view.addSubview(v)
}
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.
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];
}