iphone objective-c uiview interface-builder subclassing

iphone - Subclase UIView con su propio XIB



objective-c interface-builder (6)

Definió propietario de la xib cargada como nil . Dado que el propietario del archivo en xib tiene tomas de corriente conectadas y está definido como instancia de ShareView , obtiene la excepción sobre claves desconocidas ( nil no tiene las propiedades de salida definidas para ShareView ).

Debe definir el cargador del xib como propietario (es decir, ver el controlador responsable de cargar el xib). Luego agregue un objeto UIView separado a xib y defínalo como instancia de ShareView . Luego al cargar el xib.

ShareView *shareView = [[[[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:self options:nil] objectAtIndex:0] retain];

También puede definir shareView como un IBOutlet en la interfaz del controlador de vista (y conectar la salida del propietario del archivo a esa vista en la propia xib). Entonces, cuando cargue el xib, no habrá necesidad de reasignar la variable de instancia shareView , ya que el proceso de carga del xib volverá a conectar la vista a la variable de instancia directamente.

Esta pregunta ya tiene una respuesta aquí:

Creé una subclase UIView personalizada y preferiría no diseñar la IU en el código de la subclase UIView. Me gustaría usar un xib para eso. Así que lo que hice es lo siguiente.

Creé una clase "ShareView" que subclases UIView. Creé un archivo XIB con el propietario de su archivo configurado en "ShareView". Luego vinculo algunos puntos de venta que declaré en mi "ShareView.h".

Luego tengo un ViewController, MainViewController, que agrega el ShareView como una subvista. con este código:

NSArray *arr = [[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:nil options:nil]; UIView *fv = [[arr objectAtIndex:0] retain]; fv.frame = CGRectMake(0, 0, 320, 407); [self.view addSubview:fv];

Pero ahora obtengo errores de NSUnknownKeyException en las salidas que declaré en mi ShareView.

La razón por la que hice todo esto es porque quiero una UIView, con su propia lógica en un archivo XIB separado. Leí en varios lugares que ViewControllers solo se usa para administrar una pantalla completa, es decir, no partes de una pantalla ... Entonces, ¿qué estoy haciendo mal? Quiero mi lógica para ShareView en una clase separada, por lo que mi clase MainController no se hincha con la lógica de ShareView (¿creo que es una opción para resolver este problema?)


En lugar de subclasificar UIView, ¿por qué no subclasificar UIViewController? Echa un vistazo al siguiente enlace. En eso se hizo un UIViewControllers "RedView" y "BlueView" con sus xibs y se agregaron a la vista MultipleViewsController creando una instancia de las dos clases anteriores y agregando [self.view addSubview:red.view] y [self.view addSubview:blue.view] en el método viewDidLoad de viewDidLoad

Controladores múltiples en una vista

Simplemente agregue (id) el remitente a la función de botón presionado en RedView y BlueView en el código del enlace anterior.


Me gustaría añadir a la respuesta. Aunque espero que la gente mejore esta respuesta.

En primer lugar, funciona.

XIB:

Resultado:

Me gustaría subclasificar UIView durante mucho tiempo, especialmente para tableViewCell.

Así es como lo hice.

Es un éxito, pero en mi opinión, parte todavía es "torpe".

Primero creé un archivo .h, .m y xib habitual. Observe que Apple no tiene la casilla de verificación para crear automáticamente un xib si la subclase que creó no es una subclase de UIViewController. Bueno crearlos de todos modos.

#import <UIKit/UIKit.h> #import "Business.h" @interface BGUIBusinessCellForDisplay : UITableViewCell + (NSString *) reuseIdentifier; - (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz; @end

Un UITableViewCell realmente simple, que quiero inicializar con biz.

Pongo un reuseidentifier que deberías hacer para UITableViewCell

//#import "Business.h" @interface BGUIBusinessCellForDisplay () @property (weak, nonatomic) IBOutlet UILabel *Title; @property (weak, nonatomic) IBOutlet UIImageView *Image; @property (weak, nonatomic) IBOutlet UILabel *Address; @property (weak, nonatomic) IBOutlet UILabel *DistanceLabel; @property (weak, nonatomic) IBOutlet UILabel *PinNumber; @property (strong, nonatomic) IBOutlet BGUIBusinessCellForDisplay *view; @property (weak, nonatomic) IBOutlet UIImageView *ArrowDirection; @property (weak, nonatomic) Business * biz; @end @implementation BGUIBusinessCellForDisplay - (NSString *) reuseIdentifier { return [[self class] reuseIdentifier]; }; + (NSString *) reuseIdentifier { return NSStringFromClass([self class]); };

Luego eliminé la mayoría de los códigos de inicio y puse esto en su lugar:

- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz { if (self.biz == nil) //First time set up { self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn''t change the address self points to right NSString * className = NSStringFromClass([self class]); //PO (className); [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil]; [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it''s subview Things don''t work without it though } if (biz==nil) { return self; } self.biz = biz; self.Title.text = biz.Title; //Let''s set this one thing first self.Address.text=biz.ShortenedAddress; //if([self.distance isNotEmpty]){ self.DistanceLabel.text=[NSString stringWithFormat:@"%dm",[biz.Distance intValue]]; self.PinNumber.text =biz.StringPinLineAndNumber;

Tenga en cuenta que es realmente incómodo.

En primer lugar, el init se puede utilizar de 2 maneras.

  1. Se puede utilizar justo después de aloc.
  2. Se puede usar si tenemos otra clase existente y luego solo queremos iniciar esa celda existente en otro biz.

Así que lo hice:

if (self.biz == nil) //First time set up { self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn''t change the address self points to right NSString * className = NSStringFromClass([self class]); //PO (className); [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil]; [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it''s subview Things don''t work without it though }

Otra cosa asquerosa que hice es cuando hago [self addSubview: self.view];

La cosa es que quiero ser yo mismo la vista. No autoevaluación. De alguna manera funciona sin embargo. Así que sí, ayúdenme a mejorar, pero esa es esencialmente la manera de implementar su propia subclase de UIView.


Para usar el patrón de Yang con diseño automático, debe agregar lo siguiente en algún lugar del método -awakeWithCoder:

theRealThing.translatesAutoresizingMaskIntoConstraints = NO;

Si no desactiva -translatesAutoResizingMaskIntoConstraints, puede hacer que su diseño sea incorrecto y causar MUCHA falta de sentido de depuración en la consola.

EDITAR: Auto-diseño todavía puede ser un dolor. Ciertas restricciones no se respetan, pero otras sí lo son (por ejemplo, la fijación a la parte inferior no funciona, pero la fijación a la parte superior sí lo hace). No estamos exactamente seguros de por qué, pero puede solucionar esto al pasar manualmente las restricciones desde el marcador de posición hasta la Rehabilitación.

También vale la pena señalar que este patrón funciona de la misma manera con los guiones gráficos que con los .xibs normales (es decir, puede crear un elemento de la interfaz de usuario en un .xib y colocarlo en un controlador de StoryBoard View siguiendo sus pasos).


Puede crear su UIView personalizado diseñado en xib e incluso hacer que Interface Builder lo muestre dentro de otros archivos xib o guiones gráficos en el nuevo Xcode 6 usando IB_DESIGNABLE. En xib, establezca el propietario del archivo en su clase personalizada, pero no configure la clase UIView para evitar problemas de carga recurrentes. Simplemente deje la clase UIView predeterminada y agregará esta UIView como una subvista de su vista de clase personalizada. Conecte todos sus puntos de venta al propietario del archivo y, en su clase personalizada, cargue su xib como en el código a continuación. Puedes consultar mi video tutorial aquí: https://www.youtube.com/watch?v=L97MdpaF3Xg

IB_DESIGNABLE @interface CustomControl : UIView @end @implementation CustomControl - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { [self load]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self load]; } return self; } - (void)load { UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:@"CustomControl" owner:self options:nil] firstObject]; [self addSubview:view]; view.frame = self.bounds; } @end

Si está utilizando autolayout, es posible que desee cambiar: view.frame = self.bounds; a:

[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];


ThomasM,

Tuvimos ideas similares sobre el comportamiento de encapsulación dentro de una vista personalizada (por ejemplo, un control deslizante con etiquetas complementarias para los valores mínimo / máximo / actual, con eventos de cambio de valor también controlados internamente por el control).

En nuestra mejor práctica actual, diseñamos el ShareView en Interface Builder ( ShareView.xib ), como lo describe Eimantas en su respuesta. Luego incrustamos el ShareView a la jerarquía de vistas en MainViewController.xib .

Escribí cómo incluimos Nibs de visualización personalizada en otros Nibs en nuestro blog de desarrolladores de iOS. La -awakeAfterUsingCoder: está anulando -awakeAfterUsingCoder: en su vista personalizada, reemplaza el objeto cargado desde MainViewController.xib con el cargado desde la punta "incrustada" (ShareView.xib).

Algo a lo largo de estas líneas:

// ShareView.m - (id) awakeAfterUsingCoder:(NSCoder*)aDecoder { BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0); if (theThingThatGotLoadedWasJustAPlaceholder) { // load the embedded view from its Nib ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0]; // pass properties through theRealThing.frame = self.frame; theRealThing.autoresizingMask = self.autoresizingMask; [self release]; self = [theRealThing retain]; } return self; }