iphone - framework - Cómo cargar un UIView usando un archivo de nib creado con Interface Builder
main storyboard xcode (24)
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 según la pregunta que deba responderse.
La forma en que lo estoy haciendo es:
- Cree el objeto Question1View como una subclase
UIView
, definiendo algunosIBOutlets
. - Cree (usando Interface Builder) el
Question1View.xib
(AQUÍ ESTÁ DONDE ESTÁ MI PROBLEMA PROBABLEMENTE ). Configuré tanto elUIViewController
como elUIView
para que sean de la clase Question1View. - Enlace los puntos de venta con el componente de la vista (utilizando IB).
initWithNib
elinitWithNib
de miQuestionManagerViewController
para tener este aspecto:- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) { // Custom initialization } return self; }
Cuando ejecuto el código, recibo este error:
2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Finalizando la aplicación debido a la excepción no
NSInternalInconsistencyException
''NSInternalInconsistencyException
'', razón: ''-[UIViewController _loadViewFromNibNamed:bundle:]
cargó la-[UIViewController _loadViewFromNibNamed:bundle:]
"Question1View" pero el visor no fue establecido.
Estoy seguro de que hay una manera de cargar la vista usando el archivo nib, sin necesidad de crear una clase viewController.
@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 varios IBOutlets, algunos de los cuales están conectados a elementos en el archivo de punta original (como de costumbre), y algunos están conectados a elementos en la punta cargada. Ambas puntas tienen la misma clase de propietario. La vista cargada se superpone a la original.
Sugerencia: establezca la opacidad de la vista principal en la punta cargada a cero, luego no ocultará los elementos de la punta original.
Aquí hay una forma de hacerlo en Swift (actualmente está escribiendo Swift 2.0 en XCode 7 beta 5).
Desde su subclase UIView
que estableció como "Clase personalizada" en el Creador de interfaces, cree 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()
Después de pasar muchas horas, forjé la siguiente solución. Siga estos pasos para crear UIView
personalizado.
1) Crear la clase myCustomView heredada de UIView .
2) Crear .xib con el nombre myCustomView .
3) Cambie la clase de UIView dentro de su archivo .xib, asigne la clase myCustomView allí.
5) Cargue .xib en myCustomView * customView . Utilice el siguiente código de ejemplo.
myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];
Nota: Para aquellos que aún tengan problemas, pueden hacer comentarios, les proporcionaré el enlace del proyecto de muestra con myCustomView
Encontré esta publicación de blog de Aaron Hillegass (autor, instructor, Cocoa ninja) muy esclarecedora. Incluso si no adopta su enfoque modificado para cargar archivos NIB a través de un inicializador designado, probablemente al menos obtendrá una mejor comprensión del proceso que está ocurriendo. ¡He estado usando este método últimamente con gran éxito!
Esta es una gran pregunta (+1) y las respuestas fueron casi útiles;) Lo siento, chicos, pero pasé un buen rato repasando esto, aunque tanto Gonso como AVeryDev dieron buenas pistas. 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 la forma correctos y que esté ubicado donde lo desee. En MyVC.h, tenemos
IBOutlet MySubview *mySubView // ... @property (nonatomic, retain) MySubview *mySubview;
En MyVC.m,
@synthesize mySubView;
Y no olvides soltarlo endealloc
.- En MySubview.h, tenga una salida / propiedad 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 vista a su vista. - Coloque todos los bits y conéctese a los
IBOutlet
como desee.
- establezca el tipo de propietario del archivo en
De vuelta en MyVC.m, tengo
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];
Lo complicado para mí fue: las sugerencias en las otras respuestas cargaron mi vista desde el xib, pero NO reemplazaron la vista en MyVC (¡duh!) - Tuve que intercambiarlo por mi cuenta.
Además, para obtener acceso a los métodos de mySubview
, la propiedad de view
en el archivo .xib debe configurarse en MySubview
. De lo contrario, regresa como una UIView
.
Si hay una forma de cargar mySubview
directamente desde su propio xib, eso sería increíble, 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 una 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
Gracias a todos. Encontré la manera de hacer lo que quería.
- Crea tu
UIView
con losIBOutlet
que necesites. - Cree el xib en IB, diseñe para que le guste y vincúlelo de esta manera: El propietario del archivo es de la clase
UIViewController
(no hay 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). - Conecte sus controles con el
IBOutlet
s. El
DynamicViewController
puede ejecutar su lógica para decidir qué vista / xib cargar. Una vez que se tomó la decisión, en el métodoloadView
puso algo como esto: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.
Hice una categoría que me gusta:
UIView+NibInitializer.h
#import <UIKit/UIKit.h>
@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, llama así:
MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];
Use un nombre de punta si su punta tiene un nombre diferente al de su clase.
Para anularlo en tus subclases para un comportamiento adicional, podría tener este aspecto:
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
self = [super initWithNibNamed:nibNameOrNil];
if (self) {
self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
}
return self;
}
La respuesta anterior no tiene en cuenta un cambio en la estructura NIB (XIB) que ocurrió entre 2.0 y 2.1 del iPhone SDK. Los contenidos de los usuarios ahora comienzan 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;
Utilizamos una técnica similar a esta para la mayoría de nuestras aplicaciones.
Barney
Ninguna de las respuestas explica cómo crear el XIB autónomo que es la raíz de esta pregunta. No hay Xcode 4
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 ahorrarle algunos minutos de buscarlo ya que la palabra "XIB" no aparece en ninguna parte.
No debe configurar la clase de su controlador de vista para que sea una subclase de UIView
en Interface Builder. Eso es definitivamente al menos parte de tu problema. Deje eso como UIViewController
, alguna subclase de él o alguna otra clase personalizada que tenga.
En cuanto a cargar solo una vista desde un xib, asumí que tenía que tener algún tipo de controlador de vista (incluso si no se extiende UIViewController
, que puede ser demasiado pesado para sus necesidades) configurado como propietario del archivo en Interface Builder si quieres usarlo para definir tu interfaz. Hice una pequeña investigación para confirmar esto también. Esto se debe a que, de lo contrario, no habría manera de acceder a ninguno de los elementos de la interfaz en UIView
, ni habría una manera de que los UIView
sus propios métodos de código.
Si usa un UIViewController
como el propietario de su archivo para sus vistas, puede usar initWithNibName:bundle:
para cargarlo y recuperar el objeto del controlador de vista. En IB, asegúrese de configurar la salida de view
a la vista con su interfaz en el xib. Si usa algún otro tipo de objeto como propietario de su archivo, deberá usar el NSBundle
loadNibNamed:owner:options:
para cargar la punta, pasando una instancia del propietario del archivo al método. Todas sus propiedades se establecerán correctamente de acuerdo con las salidas que defina en IB.
No estoy seguro de a qué se refieren algunas de las respuestas, pero necesito poner esta respuesta aquí cuando busque en Google la próxima vez. Palabras clave: "Cómo cargar un UIView desde una punta" o "Cómo cargar un UIView desde un NSBundle".
Aquí está el código casi al 100% del libro de Apress Beginning 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
Extensión rápida
extension UIView {
class func loadFromNib<T>(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 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<T>(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<T>(_ 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<T>(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
}
Para el usuario Swift con opción designable:
- Cree una subclase
UIView
personalizada y un archivo xib , que nombraremos después de nuestro propio nombre de clase: en nuestro caso MemeView. Dentro de la clase Meme View, recuerde definirlo como designable con el atributo@IBDesignable
antes de la declaración de clase Rember para establecer el propietario del archivo en la xib con nuestra subclase
UIView
personalizada en el panel del inspector de indetidadEn el archivo xib ahora podemos construir nuestra interfaz, hacer restricciones, crear salidas, acciones, etc.
Necesitamos implementar algunos métodos en 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 }
}
En nuestra clase personalizada también podemos definir algunas propiedades impecables para tener control total sobre ellas desde el constructor de interfaces
@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!
}
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 generador de interfaces. Si hizo todo lo que escribí pero nada funciona, es una forma de depurar una vista sigle agregando puntos de interrupción en su implementación.
Aquí está la jerarquía de vistas.
Espero que esto ayude a descargar una muestra completa here
Para todos aquellos que necesitan administrar más de una instancia de la vista personalizada, es decir, una Colección Outlet , fusioné y personalicé las respuestas de @Gonso, @AVeryDev y @Olie de esta manera:
Cree un
MyView : UIView
yMyView : UIView
como " Clase personalizada " de laUIView
raíz en el XIB deseado;Cree todas las salidas que necesita en
MyView
(hágalo ahora porque después del punto 3, el IB le propondrá que conecte las tomas alUIViewController
y no a la vista personalizada como queremos);Configure su
UIViewController
como " Propietario del archivo " de la vista personalizada XIB ;En
UIViewController
agregue un nuevoUIViews
para cada instancia deMyView
que desee y conéctelos aUIViewController
creando una colección Outlet : estas vistas actuarán como vistas de "contenedor" para las instancias de vista personalizada;Finalmente, en el
viewDidLoad
de suUIViewController
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..
También hay una forma más fácil de acceder a la vista en lugar de tratar con la punta 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 la punta, cree una propiedad IBOutlet que contendrá la vista de la punta cargada, por ejemplo
en MyViewController (una subclase UIViewController)
@property (nonatomic, retain) IBOutlet UIView *myViewFromNib;
(No olvide sintetizarlo y liberarlo en su archivo .m)
3) abra su plumilla (lo llamaremos ''myViewNib.xib'') en IB, establezca el propietario de su 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 hagas, ¡llamar a tu propiedad "self.myViewFromNib" te dará acceso a la vista desde tu plumilla!
También puede usar el initWithNibName de UIViewController en lugar de loadNibNamed. Es más sencillo, 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, establezca la clase Propietario del archivo en UIViewController y vea la clase en MySubView.
Puede colocar y tamaño de la subview
utilizando el archivo xib principal.
Tenía motivos para hacer lo mismo (cargar una vista desde un archivo XIB mediante programación), pero tenía que hacerlo completamente desde el contexto de una subclase de una subclase de una UIView
(es decir, sin involucrar al controlador de la 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;
}
Luego lo llamo desde el método initWithFrame
de mi subclase 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ágamelo saber.
Tengo una convención de nombrar xibs con vistas en ellos igual que la vista. Lo mismo que uno haría para un controlador de vista. Entonces, no tengo que escribir nombres de clase en código. Carga 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 Interface Builder
- En Interface Builder, agregue un UIView. Establecer su clase a MyView. Personalice al contenido de su corazón, conecte las variables de instancia de MyView a las subvistas a las que desee acceder más tarde.
En su código, cree un nuevo MyView como este:
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 configuré las cosas en las etiquetas usando los puntos de venta en mi subclase de vista personalizada.
Versión más corta:
RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
Yo también quería hacer algo similar, esto es lo que encontré: (SDK 3.1.3)
Tengo un controlador de vista A (que pertenece a un controlador Nav) que carga VC B en una pulsación de 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 de Bnib, pero cuando se presiona un botón, quiero ir a un ''modo de edición'' que tiene una interfaz de usuario separada de una punta diferente, pero no quiero un nuevo VC para el modo de edición, Quiero que la nueva punta se asocie con mi B VC existente.
Por lo tanto, en BViewController.m (en el método de presionar botones)
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 estoy de vuelta a mi Bnib original.
Esto funciona bien, pero tenga en cuenta que mi EditMode.nib tiene solo 1 objeto de nivel superior, un objeto UIView. No importa si el propietario del archivo en esta punta está configurado como BViewController o el objeto NSO predeterminado, PERO asegúrese de que la Salida de visualización en el propietario del archivo NO esté configurada para nada. Si es así, obtengo un error de exc_bad_access y xcode procede a cargar 6677 cuadros de pila que muestran un método interno UIView llamado repetidamente ... así que parece un bucle infinito. (Sin embargo, el punto de vista se establece en mi Bnib original)
Espero que esto ayude.
Yo usaría UINib para crear una instancia de 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.h y 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.
En veloz
En realidad, mi resolución a este problema fue cargar la vista en un viewDidLoad en mi CustonViewController donde quería usar la vista así:
myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView
¡No cargues la vista en un método loadView()
! El método loadView sirve para cargar la vista de su ViewController personalizado.
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
explicación aquí: viewcontroller es menos vista cargando en ios & mac