Forma correcta de cargar un Nib para una subclase UIView

Soy consciente de que esta pregunta ya fue formulada pero las respuestas son contradictorias y estoy confundido, así que por favor no me llame.

Quiero tener una subclase UIView reutilizable en mi aplicación. Quiero describir la interfaz usando un archivo de punta.

Ahora digamos que es una vista de indicador de carga con un indicador de actividad. Me gustaría en algún caso instanciar esta vista y animar a la vista de un controlador de vista. Podría describir la interfaz de la vista sin ningún problema programático, crear los elementos mediante progtwigción y establecer su marco dentro de un método init, etc.

Sin embargo, ¿cómo puedo hacer esto usando una punta? Mantener el tamaño proporcionado en el constructor de interfaz sin tener que establecer un marco.

He logrado hacerlo así, pero estoy seguro de que está mal (es solo una vista con un selector):

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

Pero me estoy sobrescribiendo a mí mismo, y cuando lo instanciamos:

 FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary]; selectView.delegate = self; selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]); 

Tengo que configurar manualmente el marco en lugar de tenerlo recogido de IB.

EDITAR: Quiero crear esta vista personalizada en un controlador de vista y tener acceso para controlar los elementos de la vista. No quiero un nuevo controlador de vista.

Gracias

EDITAR: No sé si esta es la mejor práctica, estoy seguro de que no es así, pero así es como lo hice:

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

La práctica correcta todavía quería

¿Debería haber hecho esto usando un controlador de vista e iniciar con el nombre de la pluma? ¿Debo haber configurado el plumín en algún método de inicialización de UIView en el código? O es lo que he hecho bien?

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

Estoy usando esto para inicializar las vistas personalizadas reutilizables que tengo.


Tenga en cuenta que puede usar “firstObject” al final, es un poco más limpio. “firstObject” es un método útil para NSArray y NSMutableArray.

Aquí hay un ejemplo típico de cargar un xib para usar como encabezado de tabla. En tu archivo YourClass.m

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

Normalmente, en TopArea.xib , debe hacer clic en Propietario de archivo y establecer el propietario del archivo en YourClass . Entonces en realidad en YourClass.h tendrías propiedades de IBOutlet. En TopArea.xib , puede arrastrar controles a esos puntos de venta.

No olvides que en TopArea.xib , es posible que tengas que hacer clic en la vista y arrastrarlo a un punto de venta, para que tengas el control, si es necesario. (Un consejo muy valioso es que cuando haces esto para filas de celdas de tabla, tienes que hacerlo absolutamente; tienes que conectar la vista a la propiedad relevante en tu código).

Si desea mantener su CustomView y su xib independientes del File's Owner del File's Owner , siga estos pasos

  • Deje el campo File's Owner vacío.
  • Haga clic en la vista real en el archivo xib de su CustomView y configure su Custom Class como CustomView (nombre de su clase de vista personalizada)
  • Agregue IBOutlet en el archivo .h de su vista personalizada.
  • En el archivo .xib de su vista personalizada, haga clic en ver y vaya en Connection Inspector . Aquí encontrarás todos tus IBOutlets que defines en el archivo .h
  • Conéctelos con su respectiva vista.

en el archivo .m de su clase CustomView , anule el método init siguiente manera

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

Ahora, cuando desee cargar su CustomView , use la siguiente línea de código [[CustomView alloc] init];

Sigue los siguientes pasos

  1. Crea una clase llamada MyView .h / .m de tipo UIView .
  2. Crea un xib del mismo nombre MyView.xib .
  3. Ahora cambie la clase Propietario de archivo a UIViewController desde NSObject en xib. Ver la imagen de abajo enter image description here
  4. Conecte la Vista del propietario del archivo a su Vista. Ver la imagen de abajo enter image description here

  5. Cambie la clase de su Vista a MyView . Igual que 3.

  6. Los controles de lugar crean IBOutlets.

Aquí está el código para cargar la Vista:

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

Espero eso ayude.

Aclaración :

UIViewController se usa para cargar su xib y la Vista que tiene el UIViewController es en realidad MyView que ha asignado en MyView xib.

Demostración He hecho una demo aquí

Respondiendo mi propia pregunta sobre 2 o algo años más tarde aquí pero …

Utiliza una extensión de protocolo para que pueda hacerlo sin ningún código adicional para todas las clases.

 /* 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() 

En Swift:

Por ejemplo, el nombre de tu clase personalizada es InfoView

Al principio, usted crea los archivos InfoView.xib e InfoView.swift siguiente manera:

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

Luego, configure File's Owner en UIViewController siguiente manera:

enter image description here

Cambia el nombre de tu View a InfoView :

enter image description here

Haga clic derecho en File's Owner y conecte su campo de view con su InfoView :

enter image description here

Asegúrese de que el nombre de clase sea InfoView :

enter image description here

Y después de esto, puede agregar la acción al botón en su clase personalizada sin ningún problema:

enter image description here

Y el uso de esta clase personalizada en su MainViewController :

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

Bueno, podrías inicializar el xib usando un controlador de vista y usar viewController.view. o hazlo de la manera en que lo hiciste Solo hacer una subclase UIView como controlador para UIView es una mala idea.

Si no tiene ningún punto de venta desde su vista personalizada, puede usar directamente una clase UIViewController para inicializarlo.

Actualización: en tu caso:

 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 

De lo contrario, debe crear un UIViewController personalizado (para que sea el propietario del archivo, de modo que las salidas estén conectadas correctamente).

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

Donde quiera que quiera agregar la vista, puede usarla.

 [parentView addSubview:yCustCon.view]; 

Sin embargo, pasar el otro controlador de vista (que ya se usa para otra vista) como propietario mientras se carga el xib no es una buena idea ya que la propiedad de vista del controlador se cambiará y cuando desee acceder a la vista original, no lo hará tener una referencia a eso.

EDITAR: Te enfrentarás a este problema si configuraste tu nuevo xib con el propietario del archivo como la misma clase principal UIViewController y vinculó la propiedad view a la nueva vista xib.

es decir;

  • YourMainViewController – administra – mainView
  • CustomView: necesita cargarse desde xib como sea necesario.

El siguiente código causará confusión más adelante, si lo escribe dentro de la vista cargó YourMainViewController . Esto se debe a que self.view partir de ahora se referirá a su vista personalizada

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