schoolwork aula apple app ios xcode class uiview uiviewcontroller

ios - apple - aula app



¿Cómo creo una clase de vista de iOS personalizada y crea una instancia de varias copias de la misma(en IB)? (4)

Actualmente estoy haciendo una aplicación que tendrá múltiples temporizadores, que básicamente son todos iguales.

Quiero crear una clase personalizada que use todo el código para los temporizadores, así como el diseño / animaciones, para que pueda tener 5 temporizadores idénticos que operan independientemente el uno del otro.

Creé el diseño usando IB (xcode 4.2) y todo el código para los temporizadores está actualmente en la clase viewcontroller.

Tengo dificultades para entender cómo encapsular todo en una clase personalizada y luego agregarlo al controlador de vistas; cualquier ayuda sería muy apreciada.


Ejemplo de Swift

Actualizado para Xcode 9 y Swift 4

Aquí hay un recorrido básico. Originalmente aprendí mucho de esto al ver esta serie de videos de Youtube . Más tarde, actualicé mi respuesta en base a este artículo .

Agregar archivos de vista personalizados

Los siguientes dos archivos formarán su vista personalizada:

  • archivo .xib para contener el diseño
  • archivo .swift como subclase UIView

Los detalles para agregarlos están debajo.

Archivo Xib

Agregue un archivo .xib a su proyecto (Archivo> Nuevo> Archivo ...> Interfaz de usuario> Ver) . Estoy llamando al mío ReusableCustomView.xib .

Cree el diseño que desea que tenga su vista personalizada. Como ejemplo, haré un diseño con UILabel y UIButton . Es una buena idea usar el diseño automático para que las cosas cambien de tamaño automáticamente, sin importar el tamaño que establezca más adelante. (Utilicé Freeform para el tamaño xib en el inspector de Atributos para poder ajustar las métricas simuladas, pero esto no es necesario).

Archivo Swift

Agregue un archivo .swift a su proyecto (Archivo> Nuevo> Archivo ...> Fuente> Archivo Swift) . Es una subclase de UIView y llamo mina ReusableCustomView.swift .

import UIKit class ResuableCustomView: UIView { }

Haga que el archivo Swift sea el propietario

Regrese a su archivo .xib y haga clic en "Propietario del archivo" en el contorno del documento. En Identity Inspector, escriba el nombre de su archivo .swift como el nombre de clase personalizado.

Agregar código de vista personalizado

Reemplace los contenidos del archivo ReusableCustomView.swift con el siguiente código:

import UIKit class ResuableCustomView: UIView { let nibName = "ReusableCustomView" var contentView:UIView? @IBOutlet weak var label: UILabel! @IBAction func buttonTap(_ sender: UIButton) { label.text = "Hi" } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) guard let view = loadViewFromNib() else { return } view.frame = self.bounds self.addSubview(view) contentView = view } func loadViewFromNib() -> UIView? { let bundle = Bundle(for: type(of: self)) let nib = UINib(nibName: nibName, bundle: bundle) return nib.instantiate(withOwner: self, options: nil).first as? UIView } }

Asegúrese de escribir correctamente para el nombre de su archivo .xib.

Conecta los Outlets y las Acciones

Conecta las tomas de corriente y las acciones con el control arrastrando desde la etiqueta y el botón en el diseño de xib hasta el código de vista personalizado rápido.

Usa tu vista personalizada

Su vista personalizada ha terminado ahora. Todo lo que tienes que hacer es agregar un UIView donde quieras en tu guión gráfico principal. Establezca el nombre de clase de la vista en ReusableCustomView en el Inspector de identidad.

En el momento de escribir este documento, tenía problemas para agregar @IBDesignable al código, por lo que las vistas no se muestran en IB, pero sí cuando ejecutas tu aplicación. (En la imagen de arriba, le di a las vistas IB un color de fondo para que fueran visibles en el momento del diseño).


Bien para responder conceptualmente, su temporizador debería ser una subclase de UIView lugar de NSObject .

Para instanciar una instancia de su temporizador en IB simplemente arrastre un UIView en la vista de su controlador de vista, y configure su clase con el nombre de clase de su temporizador.

Recuerde #import su clase de temporizador en su controlador de vista.

Editar: para el diseño de IB (para la creación de instancias de código, ver el historial de revisión)

No estoy muy familiarizado con el guión gráfico, pero sí sé que puedes construir tu interfaz en IB usando un archivo .xib que es casi idéntico al uso de la versión del guión gráfico; Incluso debería poder copiar y pegar sus vistas como un todo desde su interfaz existente al archivo .xib .

Para probar esto, creé un nuevo .xib vacío llamado "MyCustomTimerView.xib". Luego agregué una vista y agregué una etiqueta y dos botones. Al igual que:

UIView un nuevo objetivo, clase C, subclasificación de UIView llamado "MyCustomTimer". En mi .xib configuré la clase Propietario del archivo como MyCustomTimer . Ahora puedo conectar acciones y puntos de venta como cualquier otra vista / controlador. El archivo .h resultante se ve así:

@interface MyCustomTimer : UIView @property (strong, nonatomic) IBOutlet UILabel *displayLabel; @property (strong, nonatomic) IBOutlet UIButton *startButton; @property (strong, nonatomic) IBOutlet UIButton *stopButton; - (IBAction)startButtonPush:(id)sender; - (IBAction)stopButtonPush:(id)sender; @end

El único obstáculo que queda por saltar es obtener este .xib en mi subclase UIView . El uso de .xib reduce drásticamente la configuración requerida. Y ya que está usando storyboards para cargar los temporizadores que conocemos -(id)initWithCoder: es el único inicializador que se llamará. Así que aquí es cómo se ve el archivo de implementación:

#import "MyCustomTimer.h" @implementation MyCustomTimer @synthesize displayLabel; @synthesize startButton; @synthesize stopButton; -(id)initWithCoder:(NSCoder *)aDecoder{ if ((self = [super initWithCoder:aDecoder])){ [self addSubview: [[[NSBundle mainBundle] loadNibNamed:@"MyCustomTimerView" owner:self options:nil] objectAtIndex:0]]; } return self; } - (IBAction)startButtonPush:(id)sender { self.displayLabel.backgroundColor = [UIColor greenColor]; } - (IBAction)stopButtonPush:(id)sender { self.displayLabel.backgroundColor = [UIColor redColor]; } @end

El método llamado loadNibNamed:owner:options: hace exactamente lo que parece. Carga el Nib y establece la propiedad "Propietario del archivo" para uno mismo. Extraemos el primer objeto en la matriz y esa es la vista de la raíz del Nib. Agregamos la vista como una subvista y Voila está en la pantalla.

Obviamente, esto solo cambia el color de fondo de la etiqueta cuando se presionan los botones, pero este ejemplo debería ayudarte a avanzar.

Notas basadas en comentarios:

Vale la pena señalar que si tiene problemas de recursión infinitos probablemente se haya perdido el truco sutil de esta solución. No está haciendo lo que crees que está haciendo. La vista que se coloca en el guión gráfico no se ve, sino que carga otra vista como subvista. Esa vista que carga es la vista que se define en el plumín. El "propietario del archivo" en el plumín es esa vista invisible. Lo mejor de todo es que esta vista invisible sigue siendo una clase Objective-C que se puede usar como una especie de controlador de vista para la vista que trae desde el plumín. Por ejemplo, los métodos IBAction en la clase MyCustomTimer son algo que esperaría más en un controlador de vista que en una vista.

Como nota al margen, algunos pueden argumentar que esto rompe el MVC y estoy de acuerdo en algo. Desde mi punto de vista, está más estrechamente relacionado con una UITableViewCell personalizada, que a veces también tiene que ser un controlador de parte.

También vale la pena señalar que esta respuesta fue proporcionar una solución muy específica; crea una punta que pueda ser instanciada varias veces en la misma vista que se presenta en un guión gráfico. Por ejemplo, puede imaginar fácilmente seis de estos temporizadores en una pantalla de iPad a la vez. Si solo necesita especificar una vista para un controlador de vista que se utilizará varias veces en su aplicación, la solución que ofrece jyavenard a esta pregunta es casi seguramente una mejor solución para usted.


Esta no es realmente una respuesta, pero creo que es útil compartir este enfoque.

C objetivo

  1. Importe CustomViewWithXib.h y CustomViewWithXib.m a su proyecto
  2. Cree los archivos de vista personalizados con el mismo nombre (.h / .m / .xib)
  3. Heredar su clase personalizada de CustomViewWithXib

Rápido

  1. Importe CustomViewWithXib.swift a su proyecto
  2. Cree los archivos de vista personalizados con el mismo nombre (.swift y .xib)
  3. Heredar su clase personalizada de CustomViewWithXib

Opcional :

  1. Vaya a su archivo xib, establezca el propietario con su nombre de clase personalizado si necesita conectar algunos elementos (para obtener más detalles, consulte la parte Hacer que el archivo Swift sea el propietario de las respuestas de @Suragch)

Es todo, ahora puedes agregar tu vista personalizada a tu guión gráfico y se mostrará :)

CustomViewWithXib.h :

#import <UIKit/UIKit.h> /** * All classes inherit from CustomViewWithXib should have the same xib file name and class name (.h and .m) MyCustomView.h MyCustomView.m MyCustomView.xib */ // This allows seeing how your custom views will appear without building and running your app after each change. IB_DESIGNABLE @interface CustomViewWithXib : UIView @end

CustomViewWithXib.m :

#import "CustomViewWithXib.h" @implementation CustomViewWithXib #pragma mark - init methods - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // load view frame XIB [self commonSetup]; } return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { // load view frame XIB [self commonSetup]; } return self; } #pragma mark - setup view - (UIView *)loadViewFromNib { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; // An exception will be thrown if the xib file with this class name not found, UIView *view = [[bundle loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject]; return view; } - (void)commonSetup { UIView *nibView = [self loadViewFromNib]; nibView.frame = self.bounds; // the autoresizingMask will be converted to constraints, the frame will match the parent view frame nibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // Adding nibView on the top of our view [self addSubview:nibView]; } @end

CustomViewWithXib.swift :

import UIKit @IBDesignable class CustomViewWithXib: UIView { // MARK: init methods required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonSetup() } override init(frame: CGRect) { super.init(frame: frame) commonSetup() } // MARK: setup view private func loadViewFromNib() -> UIView { let viewBundle = NSBundle(forClass: self.dynamicType) // An exception will be thrown if the xib file with this class name not found, let view = viewBundle.loadNibNamed(String(self.dynamicType), owner: self, options: nil)[0] return view as! UIView } private func commonSetup() { let nibView = loadViewFromNib() nibView.frame = bounds // the autoresizingMask will be converted to constraints, the frame will match the parent view frame nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] // Adding nibView on the top of our view addSubview(nibView) } }

Puedes encontrar algunos ejemplos here .

Espero que ayude.


Respuesta para controladores de vista, no vistas :

Hay una forma más fácil de cargar un xib desde un guión gráfico. Supongamos que su controlador es del tipo MyClassController que hereda de UIViewController .

Agrega un UIViewController utilizando IB en su guión gráfico; cambia el tipo de clase para que sea MyClassController. Elimine la vista que se agregó automáticamente en el guión gráfico.

Asegúrese de que el XIB que desea llamar se llame MyClassController.xib.

Cuando se creará una instancia de la clase durante la carga del guión gráfico, el xib se cargará automáticamente. La razón de esto se debe a la implementación predeterminada de UIViewController que llama al XIB nombrado con el nombre de clase.