Cómo cargar un UIView utilizando un archivo de punta creado con el Constructor de interfaz

Estoy tratando de hacer algo un poco elaborado, pero algo que debería ser posible. Así que aquí hay un desafío para todos los expertos que hay (este foro es un paquete de muchos de ustedes :)).

Estoy creando un “componente” de cuestionario, que quiero cargar en un NavigationContoller (mi QuestionManagerViewController ). El “componente” es un UIViewController “vacío”, que puede cargar diferentes vistas dependiendo de la pregunta que necesita ser respondida.

La forma en que lo hago es:

  1. Cree el objeto Question1View como una subclase UIView , que define algunos IBOutlets .
  2. Crear (usando el Constructor de Interfaces) el Question1View.xib (AQUÍ ES DONDE PROBABLEMENTE MI PROBLEMA ). Configuré UIViewController y UIView para que sean de la clase Question1View.
  3. Enlazo los puntos de venta con el componente de la vista (usando IB).
  4. initWithNib el initWithNib de mi QuestionManagerViewController para que se vea así:

     - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) { // Custom initialization } return self; } 

Cuando ejecuto el código, obtengo este error:

2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Aplicación de finalización debido a la excepción no NSInternalInconsistencyExceptionNSInternalInconsistencyException ‘, razón: ‘ -[UIViewController _loadViewFromNibNamed:bundle:] cargó la punta de’ Question1View ‘pero la salida de la vista no fue establecido. ‘

Estoy seguro de que hay una forma de cargar la vista usando el archivo nib, sin la necesidad de crear una clase viewController.

También hay una manera más fácil de acceder a la vista en lugar de tratar con el plumín como una matriz.

1 ) Cree una subclase de vista personalizada con cualquier salida a la que desee tener acceso más adelante. –Mi vista

2 ) en el UIViewController que desea cargar y manejar el plumín, cree una propiedad IBOutlet que contendrá la vista del plumín cargado, por ejemplo

en MyViewController (una subclase UIViewController)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib; 

(no te olvides de sintetizarlo y soltarlo en tu archivo .m)

3) abre tu plumilla (la llamaremos ‘myViewNib.xib’) en IB, configura el propietario del archivo en MyViewController

4) ahora conecte la salida de propietario de su archivo myViewFromNib a la vista principal en la punta.

5) Ahora en MyViewController, escriba la siguiente línea:

 [[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil]; 

¡Ahora tan pronto como lo haga, al llamar a su propiedad “self.myViewFromNib” le dará acceso a la vista desde su plumín!

Gracias a todos. Encontré una manera de hacer lo que quería.

  1. Crea tu UIView con los IBOutlet que necesites.
  2. Crea el xib en IB, diseñalo para que te guste y vincúlalo así: el propietario del archivo UIViewController clase UIViewController (no es una subclase personalizada, sino la “real”). La vista del propietario del archivo está conectada a la vista principal y su clase se declara como la del paso 1).
  3. Conecte sus controles con los IBOutlet s.
  4. DynamicViewController puede ejecutar su lógica para decidir qué vista / xib cargar. Una vez que tomó la decisión, en el método loadView pon algo así:

     NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil]; QPickOneView* myView = [ nibViews objectAtIndex: 1]; myView.question = question; 

¡Eso es!

El método loadNibNamed del paquete principal se encargará de inicializar la vista y crear las conexiones.

Ahora el ViewController puede mostrar una vista u otra dependiendo de los datos en la memoria, y la pantalla “principal” no necesita molestarse con esta lógica.

No estoy seguro de qué están hablando algunas de las respuestas, pero necesito poner esta respuesta aquí para cuando busque en Google la próxima vez. Palabras clave: “Cómo cargar un UIView desde un plumín” o “Cómo cargar un UIView desde un NSBundle”.

Aquí está el código casi al 100% directamente del libro de Apress Beginning para iPhone 3 (página 247, “Uso de la nueva celda de vista de tabla”):

 - (void)viewDidLoad { [super viewDidLoad]; NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah" owner:self options:nil]; Blah *blah; for (id object in bundle) { if ([object isKindOfClass:[Blah class]]) { blah = (Blah *)object; break; } } assert(blah != nil && "blah can't be nil"); [self.view addSubview: blah]; } 

Esto supone que tienes una subclase UIView llamada Blah , una punta llamada Blah que contiene una UIView que tiene su clase establecida en Blah .


Categoría: NSObject + LoadFromNib

 #import "NSObject+LoadFromNib.h" @implementation NSObject (LoadFromNib) + (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad { NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil]; for (id object in bundle) { if ([object isKindOfClass:classToLoad]) { return object; } } return nil; } @end 

Swift Extension

 extension UIView { class func loadFromNib(withName nibName: String) -> T? { let nib = UINib.init(nibName: nibName, bundle: nil) let nibObjects = nib.instantiate(withOwner: nil, options: nil) for object in nibObjects { if let result = object as? T { return result } } return nil } } 

Y un ejemplo en uso:

 class SomeView: UIView { class func loadFromNib() -> SomeView? { return self.loadFromNib(withName: "SomeView") } } 

Para todos aquellos que necesitan administrar más de una instancia de la vista personalizada, es decir, una Colección de Outlet , fusioné y personalicé las respuestas @Gonso, @AVeryDev y @Olie de esta manera:

  1. Cree un MyView : UIView personalizado MyView : UIView y MyView : UIView como ” Clase personalizada ” de la UIView raíz en el XIB deseado; clase personalizada

  2. Cree todos los puntos de venta que necesita en MyView (hágalo ahora porque después del punto 3, el IB le propondrá conectar sockets al UIViewController y no a la vista personalizada como queramos); salida de clase personalizada

  3. Configure su UIViewController como ” Propietario del archivo ” de la vista personalizada XIB ; enter image description here

  4. En UIViewController agregue una nueva UIViews para cada instancia de MyView que desee y conéctela a UIViewController creando una Colección de Outlet : estas vistas actuarán como vistas de “envoltura” para las instancias de vista personalizada; enter image description here

  5. Finalmente, en viewDidLoad de su UIViewController agregue las siguientes líneas:

 NSArray *bundleObjects; MyView *currView; NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count]; for (UIView *currWrapperView in myWrapperViews) { bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; for (id object in bundleObjects) { if ([object isKindOfClass:[MyView class]]){ currView = (MyView *)object; break; } } [currView.myLabel setText:@"myText"]; [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal]; //... [currWrapperView addSubview:currView]; [myViews addObject:currView]; } //self.myViews = myViews; if need to access them later.. 

Usaría UINib para instanciar una UIView personalizada para ser reutilizada

 UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil]; MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0]; [self.view addSubview:customView]; 

Los archivos necesarios en este caso son MyCustomView.xib , MyCustomViewClass.hy MyCustomViewClass.m Tenga en cuenta que [UINib instantiateWithOwner] devuelve una matriz, por lo que debe usar el elemento que refleja la UIView que desea reutilizar. En este caso, es el primer elemento.

No debe configurar la clase de su controlador de vista para que sea una subclase de UIView en Interface Builder. Eso definitivamente es al menos parte de tu problema. Deje eso como UIViewController , alguna subclase o alguna otra clase personalizada que tenga.

En cuanto a cargar solo una vista desde un xib, estaba bajo la suposición de que tenías que tener algún tipo de controlador de vista (incluso si no extiende UIViewController , que puede ser demasiado pesado para tus necesidades) establecido como el propietario del archivo en Interface Builder si desea usarlo para definir su interfaz. Hice una pequeña investigación para confirmar esto también. Esto se debe a que, de lo contrario, no habría forma de acceder a ninguno de los elementos de la interfaz en UIView , ni habría una forma de que tus propios métodos en el código se activen por eventos.

Si usa un UIViewController como propietario del archivo para sus vistas, puede usar initWithNibName:bundle: para cargarlo y recuperar el objeto del controlador de vista. En IB, asegúrese de establecer la salida de view a la vista con su interfaz en el xib. Si usa algún otro tipo de objeto como propietario del archivo, deberá usar el NSBundle loadNibNamed:owner:options: para cargar el pliego, pasando una instancia del propietario del archivo al método. Todas sus propiedades se establecerán correctamente según los puntos de venta que defina en IB.

También puede usar initWithNibName de UIViewController en lugar de loadNibNamed. Es más simple, me parece.

 UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil]; [self.subview addSubview:aViewController.view]; [aViewController release]; // release the VC 

Ahora solo tiene que crear MySubView.xib y MySubView.h / m. En MySubView.xib configure la clase Owner del archivo en UIViewController y vea la clase en MySubView.

Puede ubicar y dimensionar la subview utilizando el archivo xib principal.

Esta es una gran pregunta (+1) y las respuestas fueron casi útiles;) Lo siento muchachos, pero tuve un gran problema al pasar por esto, aunque tanto Gonso como AVeryDev dieron buenos consejos. Con suerte, esta respuesta ayudará a otros.

MyVC es el controlador de vista que contiene todo esto.

MySubview es la vista que queremos cargar desde un xib

  • En MyVC.xib, cree una vista de tipo MySubView que tenga el tamaño y forma correctos y MySubView donde la desee.
  • En MyVC.h, tenga

     IBOutlet MySubview *mySubView // ... @property (nonatomic, retain) MySubview *mySubview; 
  • En MyVC.m, @synthesize mySubView; y no te olvides de liberarlo en dealloc .

  • En MySubview.h, tenga un outlet / property para UIView *view (puede ser innecesario, pero funcionó para mí.) Sintetice y libérelo en .m
  • En MySubview.xib
    • establezca el tipo de propietario del archivo en MySubview y vincule la propiedad de la vista a su vista.
    • Coloque todos los pedazos y conéctelos a los IBOutlet ‘s como desee
  • De vuelta en MyVC.m, tiene

     NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL]; MySubview *msView = [xibviews objectAtIndex: 0]; msView.frame = mySubview.frame; UIView *oldView = mySubview; // Too simple: [self.view insertSubview: msView aboveSubview: mySubview]; [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting self.mySubview = msView; [oldCBView removeFromSuperview]; 

El inconveniente para mí fue que las sugerencias en las otras respuestas cargaban mi vista desde el xib, pero NO reemplazaban la vista en MyVC (¡duh!) – tuve que cambiar eso por mi cuenta.

Además, para obtener acceso a los métodos de mySubview , la propiedad de view en el archivo .xib debe establecerse en MySubview . De lo contrario, vuelve como una vieja UIView .

Si hay una manera de cargar mySubview directamente desde su propia xib, eso sería fantástico, pero esto me llevó a donde tenía que estar.

Esto es algo que debería ser más fácil. Terminé extendiendo UIViewController y agregando un loadNib:inPlaceholder: selector. Ahora puedo decir

 self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview]; 

Aquí está el código para la categoría (hace el mismo rigamarole como lo describe Gonso):

 @interface UIViewController (nibSubviews) - (UIView *)viewFromNib:(NSString *)nibName; - (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder; @end @implementation UIViewController (nibSubviews) - (UIView *)viewFromNib:(NSString *)nibName { NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; for (id view in xib) { // have to iterate; index varies if ([view isKindOfClass:[UIView class]]) return view; } return nil; } - (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder { UIView *nibView = [self viewFromNib:nibName]; [nibView setFrame:placeholder.frame]; [self.view insertSubview:nibView aboveSubview:placeholder]; [placeholder removeFromSuperview]; return nibView; } @end 

Yo también quería hacer algo similar, esto es lo que encontré: (SDK 3.1.3)

Tengo un controlador de vista A (que es propiedad de un controlador Nav) que carga VC B al presionar un botón:

En AViewController.m

 BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil]; [self.navigationController pushViewController:bController animated:YES]; [bController release]; 

Ahora VC B tiene su interfaz desde Bnib, pero cuando se presiona un botón, quiero ir a un ‘modo de edición’ que tiene una IU separada de una plumilla diferente, pero no quiero un nuevo CV para el modo de edición, Quiero que el nuevo plumín se asocie con mi B VC existente.

Entonces, en BViewController.m (en el método de presionar un botón)

 NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil]; UIView *theEditView = [nibObjects objectAtIndex:0]; self.editView = theEditView; [self.view addSubview:theEditView]; 

Luego, en otro botón, presione (para salir del modo de edición):

 [editView removeFromSuperview]; 

y he vuelto a mi Bnib original.

Esto funciona bien, pero tenga en cuenta que EditMode.nib solo tiene 1 obj de nivel superior, un obj de UIView. No importa si el propietario del archivo en este plumín está configurado como BViewController o el NSObject predeterminado, PERO asegúrese de que View Outlet en el propietario del archivo NO esté configurado en nada. Si es así, obtengo un crash exc_bad_access y xcode procede a cargar 6677 fotogtwigs astackdos que muestran un método UIView interno llamado repetidamente … por lo que parece un ciclo infinito. (Sin embargo, View Outlet está configurado en mi Bnib original)

Espero que esto ayude.

Hice una categoría que me gusta:

UIView+NibInitializer.h

 #import  @interface UIView (NibInitializer) - (instancetype)initWithNibNamed:(NSString *)nibNameOrNil; @end 

UIView+NibInitializer.m

 #import "UIView+NibInitializer.h" @implementation UIView (NibInitializer) - (instancetype)initWithNibNamed:(NSString *)nibNameOrNil { if (!nibNameOrNil) { nibNameOrNil = NSStringFromClass([self class]); } NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil owner:self options:nil]; for (id view in viewsInNib) { if ([view isKindOfClass:[self class]]) { self = view; break; } } return self; } @end 

Entonces, llame así:

 MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil]; 

Use un nombre de punta si su plumilla tiene otro nombre que no sea el nombre de su clase.

Para anularlo en sus subclases para un comportamiento adicional, podría verse así:

 - (instancetype)initWithNibNamed:(NSString *)nibNameOrNil { self = [super initWithNibNamed:nibNameOrNil]; if (self) { self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0; } return self; } 

En veloz

En realidad, mi resolución a este problema fue cargar la vista en viewDidLoad en mi CustonViewController donde quería usar la vista así:

 myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView 

¡No cargue la vista en un método loadView() ! El método loadView sirve para cargar la vista de su ViewController personalizado.

Encontré este blog publicado por Aaron Hillegass (autor, instructor, Cocoa ninja) para ser muy esclarecedor. Incluso si no adopta su enfoque modificado para cargar archivos NIB a través de un inicializador designado, probablemente obtendrá al menos una mejor comprensión del proceso que está sucediendo. ¡He estado usando este método últimamente con gran éxito!

Para usuarios de Swift con opción designable:

  1. Cree una subclase UIView personalizada y un archivo xib , que le asignaremos el nombre de nuestro propio nombre de clase: en nuestro caso, MemeView. Dentro de la clase Meme View recuerda definirlo como identificable con el atributo @IBDesignable antes de la statement de clase
  2. Rember para configurar el propietario del archivo en el xib con nuestra subclase UIView personalizada en el panel Inspector de Indetity

    enter image description here

  3. En el archivo xib ahora podemos construir nuestra interfaz, crear restricciones, crear puntos de venta, acciones, etc. enter image description here

  4. Necesitamos implementar algunos métodos para nuestra clase personalizada para abrir el xib una vez inicializado

    class XibbedView: UIView {

     weak var nibView: UIView! override convenience init(frame: CGRect) { let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! self.init(nibName: nibName) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! let nib = loadNib(nibName) nib.frame = bounds nib.translatesAutoresizingMaskIntoConstraints = false addSubview(nib) nibView = nib setUpConstraints() } init(nibName: String) { super.init(frame: CGRectZero) let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! let nib = loadNib(nibName) nib.frame = bounds nib.translatesAutoresizingMaskIntoConstraints = false addSubview(nib) nibView = nib setUpConstraints() } func setUpConstraints() { ["V","H"].forEach { (quote) -> () in let format = String(format:"\(quote):|[nibView]|") addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView])) } } func loadNib(name: String) -> UIView { let bundle = NSBundle(forClass: self.dynamicType) let nib = UINib(nibName: name, bundle: bundle) let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView return view } 

    }

  5. En nuestra clase personalizada también podemos definir algunas propiedades inspeccionables para tener un control total sobre ellas desde el constructor de la interfaz.

    @IBDesignable class MemeView: XibbedView {

     @IBInspectable var memeImage: UIImage = UIImage() { didSet { imageView.image = memeImage } } @IBInspectable var textColor: UIColor = UIColor.whiteColor() { didSet { label.textColor = textColor } } @IBInspectable var text: String = "" { didSet { label.text = text } } @IBInspectable var roundedCorners: Bool = false { didSet { if roundedCorners { layer.cornerRadius = 20.0 clipsToBounds = true } else { layer.cornerRadius = 0.0 clipsToBounds = false } } } @IBOutlet weak var label: UILabel! @IBOutlet weak var imageView: UIImageView! 

}

Algunos ejemplos:
enter image description here enter image description here

Si necesitamos agregar más información a la vista mientras se muestra dentro de un guión gráfico u otro xib, para hacer eso podemos implementar prepareForInterfaceBuilder() , este método se ejecutará solo al abrir el archivo en el constructor de interfaz. Si hiciste todo lo que escribí pero nada está funcionando, es una manera de depurar una vista sigle agregando puntos de interrupción en su implementación. enter image description here
Aquí está la jerarquía de vistas. Ver jerarquía

Espero que esto ayude a descargar una muestra completa aquí

La respuesta anterior no tiene en cuenta un cambio en la estructura NIB (XIB) que se produjo entre 2.0 y 2.1 del iPhone SDK. El contenido del usuario ahora comienza en el índice 0 en lugar de 1.

Puede usar la macro 2.1 que es válida para todas las versiones 2.1 y superiores (son dos guiones bajos antes de IPHONE:

  // Cited from previous example NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil]; int startIndex; #ifdef __IPHONE_2_1 startIndex = 0; #else startIndex = 1; #endif QPickOneView* myView = [ nibViews objectAtIndex: startIndex]; myView.question = question; 

Usamos una técnica similar a esto para la mayoría de nuestras aplicaciones.

Barney

Tenía razones para hacer lo mismo (cargando programáticamente una vista desde un archivo XIB), pero necesitaba hacerlo completamente desde el contexto de una subclase de una subclase de una UIView (es decir, sin involucrar al controlador de vista de ninguna manera). Para hacer esto, creé este método de utilidad:

 + (id) initWithNibName:(NSString *)nibName withSelf:(id)myself { NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:myself options:nil]; for (id object in bundle) { if ([object isKindOfClass:[myself class]]) { return object; } } return nil; } 

Entonces lo llamo desde mi subclase ‘ initWithFrame método así:

 - (id)initWithFrame:(CGRect)frame { self = [Utilities initWithNibName:@"XIB1" withSelf:self]; if (self) { // Initialization code. } return self; } 

Publicado por interés general; si alguien ve algún problema sin hacerlo de esta manera, házmelo saber.

Aquí hay una manera de hacerlo en Swift (actualmente escribiendo Swift 2.0 en XCode 7 beta 5).

Desde tu subclase UIView que configuraste como “Clase personalizada” en Interface Builder, crea un método como este (mi subclase se llama RecordingFooterView):

 class func loadFromNib() -> RecordingFooterView? { let nib = UINib(nibName: "RecordingFooterView", bundle: nil) let nibObjects = nib.instantiateWithOwner(nil, options: nil) if nibObjects.count > 0 { let topObject = nibObjects[0] return topObject as? RecordingFooterView } return nil } 

Entonces puedes llamarlo así:

let recordingFooterView = RecordingFooterView.loadFromNib()

Ninguna de las respuestas explica cómo crear el XIB independiente que es la raíz de esta pregunta. No hay opción de Xcode 4 para “Crear nuevo archivo XIB”.

Para hacer esto

 1) Choose "New File..." 2) Choose the "User Interface" category under the iOS section 3) Choose the "View" item 4) You will then be prompted to choose an iPhone or iPad format 

Esto puede parecer simple pero puede ahorrarte unos minutos hurgando en ello ya que la palabra “XIB” no aparece en ninguna parte.

@AVeryDev

6) Para adjuntar la vista cargada a la vista de su controlador de vista:

 [self.view addSubview:myViewFromNib]; 

Presumiblemente, es necesario eliminarlo de la vista para evitar pérdidas de memoria.

Para aclarar: el controlador de vista tiene varias IBOutlet, algunas de las cuales están conectadas a elementos en el archivo de plumilla original (como es habitual), y algunas están conectadas a elementos del plumín cargado. Ambos plumines tienen la misma clase de dueño. La vista cargada se superpone a la original.

Sugerencia: establezca la opacidad de la vista principal en la punta cargada en cero, y luego no ocultará los elementos de la punta original.

Después de pasar muchas horas, forjé la siguiente solución. Siga estos pasos para crear UIView personalizado.

1) Cree la clase myCustomView heredada de UIView .
enter image description here

2) Crea .xib con el nombre myCustomView .
enter image description here

3) Cambie la clase de UIView dentro de su archivo .xib, asigne myCustomView Class allí.
enter image description here

4) Crear IBOutlets
enter image description here

5) Cargue .xib en myCustomView * customView . Use el siguiente código de muestra.

 myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0]; [myView.description setText:@"description"]; 

Nota: Para aquellos que aún enfrentan problemas pueden hacer comentarios, les proporcionaré el enlace del proyecto de muestra con myCustomView

La versión más corta

 RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject]; 

Tengo una convención de nombrar xibs con vistas en ellos igual a la vista. Igual que uno haría para un controlador de vista. Entonces, no tengo que escribir nombres de clase en el código. Cargué un UIView desde un archivo de punta con el mismo nombre.

Ejemplo para una clase llamada MyView.

  • Cree un archivo de punta llamado MyView.xib en el Creador de interfaces
  • En Interface Builder, agregue un UIView. Establezca su clase en MyView. Personalice al contenido de su corazón, conecte las variables de instancia de MyView a las subvistas a las que le gustaría acceder más adelante.
  • En tu código, crea una nueva MyView así:

    MyView * myView = [MyView nib_viewFromNibWithOwner: owner];

Aquí está la categoría para esto:

 @implementation UIView (nib) + (id) nib_viewFromNib { return [self nib_viewFromNibWithOwner:nil]; } + (id) nib_viewFromNibWithOwner:(id)owner { NSString *className = NSStringFromClass([self class]); NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil]; UIView *view = nil; for(UIView *v in nib) { if ([v isKindOfClass:[self class]]) { view = v; break; } } assert(view != nil && "View for class not found in nib file"); [view nib_viewDidLoad]; return view; } // override this to do custom setup -(void)nib_viewDidLoad { } 

Luego conecté los botones con acciones desde el controlador que estoy usando, y establecí las cosas en las tags usando las salidas en mi subclase de vista personalizada.

Para cargar programáticamente una vista desde un nib / xib en Swift 4:

 // Load a view from a Nib given a placeholder view subclass // Placeholder is an instance of the view to load. Placeholder is discarded. // If no name is provided, the Nib name is the same as the subclass type name // public func loadViewFromNib(placeholder placeholderView: T, name givenNibName: String? = nil) -> T { let nib = loadNib(givenNibName, placeholder: placeholderView) return instantiateView(fromNib: nib, placeholder: placeholderView) } // Step 1: Returns a Nib // public func loadNib(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib { //1. Load and unarchive nib file let nibName = givenNibName ?? String(describing: type(of: placeholderView)) let nib = UINib(nibName: nibName, bundle: Bundle.main) return nib } // Step 2: Instantiate a view given a nib // public func instantiateView(fromNib nib: UINib, placeholder placeholderView: T) -> T { //1. Get top level objects let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil) //2. Have at least one top level object guard let firstObject = topLevelObjects.first else { fatalError("\(#function): no top level objects in nib") } //3. Return instantiated view, placeholderView is not used let instantiatedView = firstObject as! T return instantiatedView } 

Terminé agregando una categoría a UIView para esto:

  #import "UIViewNibLoading.h" @implementation UIView (UIViewNibLoading) + (id) loadNibNamed:(NSString *) nibName { return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1]; } + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag { NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil]; if(!nib) return nil; UIView * target = nil; for(UIView * view in nib) { if(view.tag == tag) { target = [view retain]; break; } } if(target && [target respondsToSelector:@selector(viewDidLoad)]) { [target performSelector:@selector(viewDidLoad)]; } return [target autorelease]; } @end 

explanation here: viewcontroller is less view loading in ios&mac