2010-10-27 11 views
40

Quiero crear un componente reutilizable (un control personalizado) para el iPhone. Consiste en varios controles estándar predispuestos en una Vista, y luego en algunos códigos asociados. Mis objetivos son:iPhone: crea un componente reutilizable (control) que tiene algunas piezas de Interface Builder y un código

  1. Quiero ser capaz de utilizar el Interface Builder para diseñar los subvistas en mi control personalizado;
  2. Quiero de alguna manera empaquetar todo para que luego pueda arrastrar y soltar fácilmente el componente personalizado resultante en otras vistas, sin tener que volver a conectar manualmente varias salidas, etc. (Un poco de cableado manual está bien, simplemente no quiero hacer toneladas y toneladas de esto.)

Permítanme ser más concreto, y decirle específicamente lo que mi control se supone que debe hacer. En mi aplicación, a veces necesito acceder a un servicio web para validar los datos que el usuario ingresó. Mientras espero una respuesta del servicio web, quiero mostrar un spinner (un indicador de actividad). Si los servicios web responden con un código de éxito, quiero mostrar una marca de verificación de "éxito". Si el servicio web responde con un código de error, quiero mostrar un ícono de error y un mensaje de error.

La forma de un solo uso para hacer esto es bastante sencilla: simplemente creo una UIView que contiene UIActivityIndicatorView, dos UIImages (uno para el icono de éxito y otro para el icono de error) y UILabel para el mensaje de error. Aquí hay una captura de pantalla, con las correspondientes partes marcadas en rojo:

alt text

luego de cable hasta las piezas para puntos de venta, y me puso un poco de código en mi controlador.

Pero, ¿cómo empaqueté esas piezas, el código y la pequeña colección de vistas, para poder reutilizarlas? Aquí hay algunas cosas que encontré que me consigue a medias, pero no son tan grandes:

  • puedo arrastrar la colección de puntos de vista y controles en la sección de objetos personalizados de la Biblioteca; luego, más tarde, puedo arrastrarlos a otras vistas. Pero (a) olvida qué imágenes se asociaron con los dos UIImages, (b) hay una gran cantidad de cableado manual de cuatro o cinco enchufes, y (c) lo más importante, esto no lleva el código. (¿Quizás haya una manera fácil de conectar el código?)
  • Creo que podría crear un IBPlugin; no estoy seguro si eso ayudaría, y parece que hay mucho trabajo, y tampoco me queda del todo claro si IBPlugins funciona para el desarrollo de iPhone.
  • Pensé, "Hmm, hay un código asociado a esto - que huele como un controlador", así que traté de crear un controlador personalizado (por ejemplo, WebServiceValidatorController) con el archivo XIB asociado. Eso en realidad parece muy prometedor, pero en ese momento no puedo entender cómo, en Interface Builder, arrastrar este componente a otras vistas. El WebServiceValidatorController es un controlador, no una vista, por lo que puedo arrastrarlo a una ventana de documento, pero no a una vista.

Tengo la sensación de que me falta algo obvio ...

+0

He encontrado este enlace para proporcionar buena información: http://www.bartlettpublishing.com/site/bartpub/blog/3/entry/327 –

Respuesta

12

He creado construcciones similares, excepto que yo no uso el resultado en IB, pero crear una instancia utilizando el código. Describiré cómo funciona eso, y al final le daré una pista de cómo se puede usar para lograr lo que busca.

Empiezo desde un archivo XIB vacío donde agrego una vista personalizada en el nivel superior. Configuro esa vista personalizada para ser mi clase.A continuación, en la jerarquía de vista, creo y configuro subvistas según sea necesario.

Creo todos IBOutlets en mi clase de vista personalizada, y los conecto allí. En este ejercicio, ignoro por completo al "propietario del archivo".

Ahora, cuando necesito para crear la vista (por lo general en el controlador como parte de tiempo/ciclo for para crear la mayor cantidad de ellos, según sea necesario), utilizo la funcionalidad de NSBundle así:

- (void)viewDidLoad 
{ 
    CGRect fooBarViewFrame = CGRectMake(0, 0, self.view.bounds.size.width, FOOBARVIEW_HEIGHT); 
    for (MBSomeData *data in self.dataArray) { 
     FooBarView *fooBarView = [self loadFooBarViewForData:data]; 
     fooBarView.frame = fooBarViewFrame; 
     [self.view addSubview:fooBarView]; 

     fooBarViewFrame = CGRectOffset(fooBarViewFrame, 0, FOOBARVIEW_HEIGHT); 
    } 
} 

- (FooBarView*)loadFooBarViewForData:(MBSomeData*)data 
{ 
    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FooBarView" owner:self options:nil]; 
    FooBarView *fooBarView = [topLevelObjects objectAtIndex:0]; 
    fooBarView.barView.amountInEuro = data.amountInEuro; 
    fooBarView.barView.gradientStartColor = data.color1; 
    fooBarView.barView.gradientMidColor = data.color2; 
    fooBarView.titleLabel.text = data.name; 
    fooBarView.titleLabel.textColor = data.nameColor; 
    return fooBarView; 
} 

Aviso , cómo configuré el propietario de la punta en self - no hay daño ya que no conecté el "propietario del archivo" a nada. El único resultado valioso al cargar esta punta es su primer elemento: mi vista.

Si desea adaptar este enfoque para crear vistas en IB, es bastante fácil. Implementar la carga de subvista en una vista personalizada principal. Establezca el marco de la subvista en los límites de la vista principal, de modo que tengan el mismo tamaño. La vista principal se convertirá en el contenedor para su vista personalizada real, y también una interfaz para código externo; usted solo abre las propiedades necesarias de sus subvistas, el resto está encapsulado. A continuación, solo suelta la vista personalizada en IB, configúrela para que sea su clase y la use como de costumbre.

+1

¡Claramente escrito, listo, y funciona! Gracias Michal. –

+1

No debe depender del orden de los objetos devueltos por loadNibNamed: owner: options :, en su lugar, debe verificar los valores de clase/etiqueta en los objetos devueltos. Una de las ventajas de utilizar el propietario y los puntos de venta del archivo tal como lo describí en mi respuesta es que no es necesario recorrer los objetos devueltos por este método. – Bogatyr

+0

bueno, estoy usando el hecho de que agregué solo un objeto de nivel superior al XIB, y dado que solo hay uno, es bastante seguro no iterar, sino recoger el primero. – Michal

21

Aquí es cómo estoy resolviendo un problema similar: quería:

  • crear "clases del módulo de widget" autónomos reutilizables implementación de una visión compleja construida a partir de múltiples componentes UIKit (y otra anidada clases de módulo de widgets!). A las clases de clientes de nivel superior de estas clases de widgets no les importa qué es UILabel, qué es un UIImageView internamente dentro del widget, a las clases de clientes solo les importan conceptos como "mostrar el puntaje" o "mostrar el logotipo del equipo".

  • dentro de una clase módulo flash, diseñar la interfaz de usuario para el widget y medios de transmisión en circuito utilizando la interfaz constructor

  • para los clientes de nivel superior de un widget, que quería ser capaz de colocar el marco del widget dentro de la vista del cliente en el constructor de interfaces sin tener que diseñar complementos personalizados a IB, etc.

Estos widgets no son controladores de nivel superior: por lo que no tiene sentido para que sean subclases de UIViewController. Luego también está el consejo de Apple de no tener más de un VC en una pantalla visible a la vez. Además, hay tantos consejos y opiniones flotando como "MVC! MVC! Debes separar tu Vista y tu control! MVC!" que se desalienta tanto a las personas a subclasificar a UIView o colocar la lógica de la aplicación dentro de una subclase de UIView.

Pero decidí hacerlo de todos modos (subclase UIView). He estado alrededor del bloque unas cuantas veces (pero todavía bastante nuevo en el SDK/UIKit de iPhone), y soy muy sensible al diseño que es feo, y eso puede causar problemas, y francamente no veo el problema con la subclasificación de UIView y la adición de puntos de venta y acciones. De hecho, hay muchas ventajas a hacer su widget de reutilizable sea una subclase de UIView en lugar de basarse en la UIViewController:

  • Puede colocar una instancia directa de la clase de widget en el diseño constructor de interfaces de una vista del cliente, y el El marco UIView de widgets se inicializará correctamente cuando la clase de cliente se cargue desde el xib. Esto es mucho más limpio para mí que poner una "vista de marcador" en el cliente, luego instanciar el módulo de widget programáticamente y establecer el marco del widget en el marcador de posición, y luego intercambiar la vista de marcador de posición para la vista del widget.

  • Puede crear un archivo xib limpio y simple para diseñar los componentes del widget en el constructor de interfaz. El propietario del archivo está configurado para su clase de widget. El archivo nib contiene una UIView raíz (vainilla) donde se presenta toda la GUI, y luego en el método awakeFromNib: del widget, esta vista de punta se agrega al widget como una subvista. Cualquier ajuste de tamaño de fotograma se puede manejar aquí, completamente dentro del código del widget, si es necesario. De este modo:

- (void) awakeFromNib 
{ 
    [[NSBundle mainBundle] loadNibNamed:@"MyWidgetView" owner:self options:nil]; 
    [self addSubview:self.view]; 
} 
  • El widget de auto-initialzes su interfaz de usuario mediante el uso de loadNibNamed de NSBundle: método de opciones en su propio método awakeFromNib, por lo que la integración del control dentro de las clases de clientes: Propietario está limpio: solo coloque un UIView en la vista del cliente en IB, cambie la clase de UIView a MyWidgetView, y en el init del cliente o viewDidLoad configure los valores predeterminados en la pantalla del widget usando la API del widget que usted mismo escribe (setScore: setTeamImage: etc .)

Me parece que esencialmente no hay diferencia en el código resultante entre la subclasificación de una UIView de esta manera para hacer un "widget" reutilizable, y el uso de un UIViewController. En ambos casos, los archivos .h contienen miembros de clase de widget, puntos de venta, declaraciones de acción y declaraciones de API especiales. En ambos casos, el archivo .m contiene implementaciones de acción e implementación especial de API. ¡Y la vista está separada del control, la vista está en el archivo xib!

Así, en resumen, esto es lo que hago para el envasado hasta complejas reutilizables Vea los aparatos:

  • hacer que la clase widget de una subclase de UIView
  • Instantiate el widget donde quieras, en otras vistas en su aplicación en IB directamente arrastrando un UIView desde la biblioteca de herramientas y cambiando la clase a su "MyWidgetClass".
  • Diseña la IU de tu widget en IB en un plumín dentro de una raíz UIView.
  • en awakeFromNib de la clase Widget: método, cargar la interfaz de usuario del widget de xib y hacer [self addSubview:self.theXibRootView]

  • Código el widget igual que lo haría un UIViewController: puntos de venta, las acciones, las APIs

Me interesaría saber de otros sistemas estructurales para crear widgets reutilizables y cuáles son las ventajas de este enfoque.

Si el widget necesita reportar eventos a la clase de cliente, sólo tiene que utilizar un protocolo delegado donde el cliente establece el delegado del widget a uno mismo, al igual que en un UIViewController.

+2

Gracias. Esto es justo lo que estaba buscando. – jlstrecker

Cuestiones relacionadas