Soy relativamente nuevo en el XCode/iOS mundo; yo lo he hecho algunas de tamaño decente guión basado en las aplicaciones, pero yo no me corte los dientes en el conjunto de la punta/xib cosa. Quiero usar las mismas herramientas para escenas para el diseño reutilizable ver/controlar. Así que he creado mi primera vez xib para mi punto de vista de la subclase y pintado de seguridad:

Cómo incrustar una vista personalizada xib en un storyboard de la escena?

Tengo mi tomacorrientes conectados y las limitaciones de la instalación, así como yo estoy acostumbrado a hacer en el guión gráfico. I conjunto de la clase de mi File Owner a la de mi costumbre UIView subclase. Así que asumo que puede ejemplificar este punto de vista de la subclase con algunas API, y se configura/conectado como se muestra.

Ahora, de vuelta en mi guión, quiero embed/reutilización de este. Estoy haciendo esto en una vista de tabla prototipo de celda:

Cómo incrustar una vista personalizada xib en un storyboard de la escena?

Tengo una vista. He conjunto de la clase a mi subclase. He creado un enchufe para para que yo lo pueda manipular.

Los $64 pregunta es ¿dónde/cómo puedo indicar que no es suficiente con simplemente poner un vacío/no configurado instancia de mi punto de vista subclase de allí, pero para el uso .xib que he creado para configurar/crear instancias de ella? Sería genial, si en XCode6, yo sólo podía entrar en el XIB archivo a utilizar para un determinado UIView, pero no veo un campo para hacer eso, así que supongo que tengo que hacer algo en el código en algún lugar.

(Hago ver otras preguntas como esta ASÍ, pero no he encontrado ninguna pidiendo esta parte del rompecabezas, o hasta la fecha con XCode6/2015)

Actualización

Soy capaz de conseguir este tipo de trabajo por la aplicación de mi celda de la tabla del awakeFromNib de la siguiente manera:

- (void)awakeFromNib
{
    //gather all of the constraints pointing to the uncofigured instance
    NSArray* progressConstraints = [self.contentView.constraints filteredArrayUsingPredicate: [NSPredicate predicateWithBlock:^BOOL(id each, NSDictionary *_) {
        return (((NSLayoutConstraint*)each).firstItem == self.progressControl) || (((NSLayoutConstraint*)each).secondItem == self.progressControl);
    }]];
    //fetch the fleshed out variant
    ProgramProgressControl *fromXIB = [[[NSBundle mainBundle] loadNibNamed:@"ProgramProgressControl" owner:self options:nil] objectAtIndex:0];
    //ape the current placeholder's frame
    fromXIB.frame = self.progressControl.frame;
    //now swap them
    [UIView transitionFromView: self.progressControl toView: fromXIB duration: 0 options: 0 completion: nil];
    //recreate all of the constraints, but for the new guy
    for (NSLayoutConstraint *each in progressConstraints) {
        id firstItem = each.firstItem == self.progressControl ? fromXIB : each.firstItem;
        id secondItem = each.secondItem == self.progressControl ? fromXIB : each.secondItem;
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem: firstItem attribute: each.firstAttribute relatedBy: each.relation toItem: secondItem attribute: each.secondAttribute multiplier: each.multiplier constant: each.constant];
        [self.contentView addConstraint: constraint];
    }
    //update our outlet
    self.progressControl = fromXIB;
}

Es esto tan fácil como se pone entonces? O estoy trabajando muy duro para esto?

  • 1) Usted no debe especificar los Archivos del Propietario, porque no tiene dueño del Archivo si xib es sólo vista personalizada. Cuando se carga punta propietario parámetro debe ser nulo. 2) Usted debe especificar la Clase Personalizada para su vista en su lugar. Y conecte las salidas de este punto de vista

11 Comentarios

  1. 27

    Usted está casi allí. Usted necesita para reemplazar initWithCoder en su clase personalizada que le asigna la vista.

    - (id)initWithCoder:(NSCoder *)aDecoder {
        if ((self = [super initWithCoder:aDecoder])) {
            [self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"ViewYouCreated" owner:self options:nil] objectAtIndex:0]];
        }
        return self; }

    Una vez hecho el Guión gráfico se sabe que la carga de la xib interior que UIView.

    He aquí una explicación más detallada:

    Esta es la forma en que su UIViewController parece en su placa de la historia:
    Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    El espacio azul es básicamente una UIView que se «espera» que su xib.

    Esta es tu xib:

    Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    Hay una Acción conectado a un botón que va a imprimir el texto.

    y este es el resultado final:

    Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    La diferencia entre el primer clickMe y la segunda es que la primera fue añadido a la UIViewController el uso de la StoryBoard. El segundo fue añadido mediante código.

    • No sé si funcionaría, pero no podía agregar la vista en layoutSubviews en lugar de initWithCoder: y hacer que la clase IB_DESIGNABLE? Supongo que dependerá de si IB_DESIGNABLE clases son capaces de carga de la agrupación
    • No sé si he aplicado mal tu respuesta, pero no funcionó como se esperaba. En un caso, la puse en el ProgramProgressControl clase que el .xib ha registrado como el propietario del archivo. En ese caso, se produjo una excepción en el super enviar. En el otro, la puse en mi celda de la tabla de la subclase, donde este va a ser colocado. Las opiniones tipo de se presentó, pero no estaban aún dentro de la cáscara, y no me aparecen para ser conectado a los puntos de venta.
    • No estoy seguro de cómo funciona el código, pero he encontrado un ejemplo completo para que se demuestra cómo initWithCoder and un xib archivo de obras. stackoverflow.com/questions/9251202/…
    • He editado mi respuesta con más ejemplos y un proyecto de ejemplo que puede jugar.
    • Así que básicamente, usted tiene que utilizar un 2 ver la solución. Es eso correcto? El guión tiene un punto de vista que usted tiene una toma de corriente, y este punto de vista tiene ninguno de los atributos que se aplican en el guión gráfico (diseño, color, etc). Y, a continuación, cargar el XIB instancias de vista como el único subvista de ese punto de vista. Lo que yo estaba tratando de hacer era swap/fusionar el «marcador de posición» ver en el guión gráfico con el que sacó de la XIB. Estoy pensando en que no es posible, y este doble punto de vista es el mejor camino a seguir. Es eso correcto?
    • Sí. Usted no puede combinar dos vistas de utilizar el constructor de interfaces. Mismo con el intercambio. A pesar de que nuestro marcador de posición de Clase Personalizada DoSomething tendrá la xib como una subvista con un archivo propietario de la misma clase y ese es el «truco». Sin embargo, puede reemplazar un UIView con un xib utilizando sólo el código, sino que derrota el Propósito de esta pregunta.
    • O, en lugar de reemplazar initWithCoder, puede reemplazar awakeAfterUsingCoder: y hacer una vista real de intercambio. Misma cantidad de trabajo, un mejor resultado.

  2. 16

    Que usted necesita para implementar awakeAfterUsingCoder: personalizado UIView subclase. Este método permite el intercambio de la decodificado objeto (desde el guión gráfico) con un objeto diferente (de su reutilizables xib), así:

    - (id) awakeAfterUsingCoder: (NSCoder *) aDecoder
    {
        //without this check you'll end up with a recursive loop - we need to know that we were loaded from our view xib vs the storyboard.
        //set the view tag in the MyView xib to be -999 and anything else in the storyboard.
        if ( self.tag == -999 )
        {
            return self;
        }
    
        //make sure your custom view is the first object in the nib
        MyView* v = [[[UINib nibWithNibName: @"MyView" bundle: nil] instantiateWithOwner: nil options: nil] firstObject];
    
        //copy properties forward from the storyboard-decoded object (self)
        v.frame = self.frame;
        v.autoresizingMask = self.autoresizingMask;
        v.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
        v.tag = self.tag;
    
        //copy any other attribtues you want to set in the storyboard
    
        //possibly copy any child constraints for width/height
    
        return v;
    }

    Hay una muy buena valoración crítica aquí discutir esta técnica y un par de alternativas.

    Además, si agrega IB_DESIGNABLE a su @interfaz de declaración, y proporcionar un initWithFrame: método se puede obtener en tiempo de diseño vista previa para trabajar en IB (Xcode 6 necesario!):

    IB_DESIGNABLE @interface MyView : UIView
    @end
    
    @implementation MyView
    
    - (id) initWithFrame: (CGRect) frame
    {
        self = [[[UINib nibWithNibName: @"MyView"
                                bundle: [NSBundle bundleForClass: [MyView class]]]
    
                 instantiateWithOwner: nil
                 options: nil] firstObject];
    
        self.frame = frame;
    
        return self;
    }
    • Este es sin duda preferible a la de «utilizar una segunda envoltura de ver y agregar su vista real como una subvista en initWithCoder» slapdashery. Pesada contexto de la portabilidad se puede hacer en una categoría/método de extensión. Una sugerencia para quitar un poco de la configuración: es de suponer que usted tiene subvistas en el xib, de lo contrario no tendría sentido, así que uno podría depender de subvistas vacío o no en lugar de la etiqueta de propiedad.
  3. 15

    Una genial y reutilizables manera de hacer esta Interface Builder y Swift 4:

    1. Crear una nueva clase así:

      import Foundation
      import UIKit
      
      @IBDesignable class XibView: UIView {
      
          @IBInspectable var xibName: String?
      
          override func awakeFromNib() { 
              guard let name = self.xibName, 
                    let xib = Bundle.main.loadNibNamed(name, owner: self), 
                    let view = xib.first as? UIView else { return }    
              self.addSubview(view)
          }
      
      }
    2. En su guión gráfico, agregar un UIView que actuará como contenedor de la Xib. Asígnele un nombre de clase de XibView:

      Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    3. En el inspector de propiedades de este nuevo XibView, establecer el nombre del dispositivo .xib (sin la extensión del archivo) en el IBInspectable campo:

      Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    4. Añadir un nuevo Xib ver a su proyecto, y en el inspector de propiedades, establezca el Xib del «Dueño del Archivo» para XibView (asegurarse de que sólo el «Dueño del Archivo» a tu clase personalizada, NO subclase de la vista de contenido, o se estrellará), y de nuevo, establece la IBInspectable campo:

      Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    Una cosa a tener en cuenta: Esto supone que estás coincidencia el .xib marco de su contenedor. Si no, o de la necesidad a ser de tamaño variable, tendrás que añadir en algunos programática restricciones o modificar la subvista del marco para encajar. Yo uso para hacer las cosas fáciles:

    xibView.snp_makeConstraints(closure: { (make) -> Void in
        make.edges.equalTo(self)
    })

    Puntos de bonificación

    Supuestamente puede utilizar prepareForInterfaceBuilder() para hacer estos reutilizables vistas visible en el Interface Builder, pero no he tenido mucha suerte. Este blog sugiere agregar un contentView de la propiedad, y llamando a los siguientes:

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        xibSetup()
        contentView?.prepareForInterfaceBuilder()
    }
    • Bastante interesante la solución, aunque estoy recibiendo una advertencia: «IB Designables: Ignorando definido por el usuario en tiempo de ejecución atributo de ruta de la clave «xibName» en la instancia de «UIView». Golpear una excepción al intentar establecer su valor: [<UIView 0x7f8ac2f18f30> setValue:forUndefinedKey:]: esta clase no es el valor de la clave de codificación compatible con la tecla de xibName.»
    • Hrm parece que tal vez tenga un error tipográfico en el nombre de algún lugar? Mi primera conjetura es capital X XibView.
    • No lo creo, porque de su solución funciona muy bien, a mi XIB es, definitivamente, de ser cargado. Si la vista no tenía su clase personalizada establece correctamente que no funcionaría en absoluto. Es sólo la advertencia de que es molesto.
    • Oh extraño. Si usted averiguar déjame saber.
    • if let views = xib as? [UIView] where views.count > 0 en swift ha cambiado la sintaxis para if let views = xib as? [UIView], views.count > 0
    • también puede cambiar self.addSubview(xib[0] as! UIView) a self.addSubview(views[0] as! UIView)
    • Niza – actualizado gracias!
    • Un formato mejor para awakeFromNib sería el uso de un protector de instrucción en lugar de 3 en el caso de declaraciones para evitar la anidación de ejemplo override func awakeFromNib() { guard let name = self.xibName, let xib = Bundle.main.loadNibNamed(name, owner: self), let views = xib as? [UIView], views.count > 0 else { return } self.addSubview(views[0] as! UIView) }
    • Disculpas por los pobres de formato, no estoy seguro de cómo el formato de los comentarios de trabajo
    • Lol no. Gran adición a pesar de que! Me encanta guard.

  4. 2

    Sólo tienes que arrastrar y soltar UIView en su IB, y de la toma de corriente y

    yourUIViewClass  *yourView =   [[[NSBundle mainBundle] loadNibNamed:@"yourUIViewClass" owner:self options:nil] firstObject];
    [self.view addSubview:yourView]

    Cómo incrustar una vista personalizada xib en un storyboard de la escena?

    Paso

    1. Agregar Nuevo Archivo => Interfaz de Usuario => UIView
    2. Conjunto de clases Personalizadas – yourUIViewClass
    3. Conjunto de Restauración ID – yourUIViewClass
    4. yourUIViewClass *yourView = [[[NSBundle mainBundle] loadNibNamed:@"yourUIViewClass" owner:self options:nil] firstObject];
      [self.view addSubview:yourView]

    Ahora usted puede personalizar la vista como desee.

    • Tengo para establecer el valor de la toma de corriente antes de un viewDidLoad? O después? O no importa?
    • después de viewDidLoad: porque si la vista no está cargado todavía no se puede agregar otro subvista en él.
    • Lo que estamos mostrando aquí es cómo agregar a ver que se recuperan de una XIB como un subvista a una vista que he creado en un guión y tienen un outlet a. Lo cual es genial. Pero lo que yo quería es reemplace la vista en el guión gráfico con la XIB uno. IOW, quiero usar la vista en el guión como un marcador de posición. Dicho esto, me gustaría que las propiedades aplicadas a la vista en el guión gráfico (cosas como el color de fondo, diseño, etc) que todavía se aplican.
  5. 2
    • Crear archivo xib
      File > New > New File > iOS > User Interface > View
    • Crear UIView clase
      File > New > New File > iOS > Source > CocoaTouch
    • Asignar el archivo xib la identidad de la costumbre de la clase de vista
    • En viewDidLoad del controlador de vista de inicializar el xib y su archivo asociado el uso de loadNibNamed: en NSBundle.mainBundle y el primer punto de vista se devuelve puede ser añadido como una subvista de sí mismo.ver.
    • La vista personalizada carga de la punta puede ser guardado en una propiedad para establecer el marco en viewDidLayoutSubviews. Acaba de establecer el marco para self.view‘s fotograma a menos que usted necesita para hacer la menor de self.view.

      class ViewController: UIViewController {
      
          weak var customView: MyView!
      
          override func viewDidLoad() {
              super.viewDidLoad()
              self.customView = NSBundle.mainBundle().loadNibNamed("MyView", owner: self, options: nil)[0] as! MyView
              self.view.addSubview(customView)
              addButtonHandlerForCustomView()
          }
      
          private func addButtonHandlerForCustomView() {
              customView.buttonHandler = {
                  [weak self] (sender:UIButton) in
                  guard let welf = self else {
                      return
                  }
                  welf.buttonTapped(sender)
              }
          }
      
          override func viewDidLayoutSubviews() {
              self.customView.frame = self.view.frame
          }
      
          private func buttonTapped(button:UIButton) {
      
          }
      }
    • También, si quieres hablar de nuevo de la xib a su UIViewController instancia, a continuación, crear una débil propiedad en la vista personalizada de la clase.

      class MyView: UIView {
      
          var buttonHandler:((sender:UIButton)->())!
      
          @IBAction func buttonTapped(sender: UIButton) {
              buttonHandler(sender:sender)
          }
      }

    Aquí está el proyecto en GitHub

  6. 2

    He estado usando este fragmento de código para años. Si usted planea en tener clase personalizada vistas en su XIB acaba de caer este en el .m archivo de la clase personalizada.

    Como un efecto secundario que se traduce en awakeFromNib ser llamado así que usted puede dejar a todos sus init/código de configuración de allí.

    - (id)awakeAfterUsingCoder:(NSCoder*)aDecoder {
        if ([[self subviews] count] == 0) {
            UIView *view = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil][0];
            view.frame = self.frame;
            view.autoresizingMask = self.autoresizingMask;
            view.alpha = self.alpha;
            view.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
            return view;
        }
        return self;
    }
  7. 1

    Un poco más swifty versión de @brandonscript ‘s idea con un retorno temprano:

    override func awakeFromNib() {
    
        guard let xibName = xibName,
              let xib = Bundle.main.loadNibNamed(xibName, owner: self, options: nil),
              let views = xib as? [UIView] else {
            return
        }
    
        if views.count > 0 {
            self.addSubview(views[0])
        }
    
    }
  8. 0

    Mientras yo no recomiendo la ruta que va hacia abajo, esto se puede hacer mediante la colocación de un «integrado controlador de vista de vista de» ¿dónde quieres que la vista aparecen.

    Incrustar una vista controlador que contiene una sola vista: la vista que desea ser reutilizados.

    • Esto funciona hasta que se quieren poner en cosas como las celdas de la tabla 🙂
  9. 0

    Ha sido un tiempo en esto, y he visto un número de respuestas a ir. Recientemente he retomado porque yo había estado usando UIViewController incrustación. Que funciona, hasta que usted quiere poner algo en «un elemento repetido en tiempo de ejecución» (por ejemplo, un UICollectionViewCell o un UITableViewCell). El enlace proporcionado por @TomSwift me llevó a seguir el patrón de

    A) en Lugar de hacer la vista principal de la clase de ser la costumbre tipo de clase, hacer el FileOwner ser la clase de destino (en mi ejemplo, CycleControlsBar)

    B) Cualquier toma de corriente/acción de vinculación de la anidados widgets va a

    C) poner en práctica este sencillo método en CycleControlsBar:

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        if let container = (Bundle.main.loadNibNamed("CycleControlsBar", owner: self, options: nil) as? [UIView])?.first {
            self.addSubview(container)
            container.constrainToSuperview() //left as an excise for the student
        }
    }

    Funciona como un encanto y mucho más sencillo que los otros enfoques.

  10. 0

    He aquí la respuesta que te he querido. Usted puede crear su CustomView clase, tiene el maestro de instancia en un xib con todas las subvistas y puntos de venta. A continuación, puede aplicar esa clase con todas las instancias en sus guiones o otros xibs.

    Sin necesidad de trastear con el Propietario del Archivo, o conectar las salidas de un proxy o modificar el xib de una manera peculiar, o agregar una instancia de la vista personalizada como una subvista de sí mismo.

    Basta con hacer:

    1. De importación BFWControls marco
    2. Cambiar su superclase de UIView a NibView (o de UITableViewCell a NibTableViewCell)

    Que es!

    Incluso funciona con IBDesignable para representar la vista personalizada (incluyendo las subvistas de la xib) en tiempo de diseño en el guión gráfico.

    Usted puede leer más sobre esto aquí:
    https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

    Y usted puede conseguir el código fuente abierto BFWControls marco de aquí:
    https://github.com/BareFeetWare/BFWControls

    Y he aquí un extracto de código que lo impulsa, en caso de que usted es curioso:
    https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

    Tom 👣

  11. -2

    La respuesta «correcta» es que usted no quiere hacer la re-utilizable puntos de vista con sus correspondientes puntas. Si una vista de la subclase es valioso como un objeto reutilizable rara vez se necesita una punta para ir con ella. Tomemos, por ejemplo, cada vista subclase proporcionada por UIKit. Parte de este pensamiento es una vista de la subclase que es realmente valioso no será implementado usando una punta, que es la opinión general en Apple.

    Generalmente cuando se utiliza una vista en la punta o guión gráfico que se desea ajustar gráficamente para el caso de uso de todos modos.

    Puede considerar el uso de «copiar y pegar» para recrear la misma o puntos de vista similares, en lugar de hacer separados puntas. Yo este cree que cumple los mismos requisitos y que se mantendrá más o menos en línea con lo que Apple está haciendo.

    • No estoy siguiendo tu argumento. Usted parece estar diciendo «si es reutilizable, sólo el código de todo como una clase (diseño y todas)», que si ese fuera el caso, ¿por qué no son todos los de la vista de los controladores de hecho de esa manera, así como cualquier útil de vista de la jerarquía?
    • Esta es una perspectiva interesante — que re-utilizable puntos de vista siempre debe ser implementado en código, y que xibs y los storyboards son sólo para el montaje de la no re-utilizable vistas desde la re-utilizable vistas. Es bastante coherente con lo que las herramientas permiten. Pero, ¿tiene alguna otra razón para decir que esta es «la opinión general en Apple»?
    • Creo que el punto es encontrar una manera de hacerlo, a pesar de la opinión general en Apple. Este enfoque parece contraproducente; la reutilización de UIView xibs que han configuración modular es bastante común y tiene un montón de aplicaciones. El «¿por qué querríamos hacer esto?» es ineficiente para cualquiera que la construcción de una seria aplicación que necesariamente va a tener que ir en contra de «lo que Apple está haciendo» en algunos casos. (a menos que seas desarrollo de código para Apple, es)
    • Travis: estoy diciendo que si una vista que se ha diseñado en el Interface Builder es útil hay una manera mucho más fácil para duplicar. De seleccionarlo, pulse comando-C, a continuación, pulse comando-V. Muy pocos pasos en comparación con la localización de la misma en una punta. Si usted tiene una vista que es exactamente de la misma en todas partes, es utilizado en muchos lugares, y que desea ser capaz de modificar fácilmente que hay una nueva manera de hacer eso. Pero en muchas, muchas aplicaciones para las que he trabajado en este simplemente no llegar. DivideByZer0: Es útil en la que el autor está tratando de forzar a su flujo de trabajo en las herramientas en lugar de adoptar las herramientas.

Dejar respuesta

Please enter your comment!
Please enter your name here