Quiero usar una vista en varios controles de vista en un guión gráfico. Por lo tanto, pensé en diseñar la vista en un xib externo para que los cambios se reflejen en cada controlador de vista. Pero, ¿cómo se puede cargar una vista desde un xib externo en un guión gráfico e incluso es posible? Si ese no es el caso, ¿qué otras alternativas están disponibles para adaptarse a la situación anterior?
ios
uiview
storyboard
xib
Sebastian Hoffmann
fuente
fuente
Respuestas:
Mi ejemplo completo está aquí , pero proporcionaré un resumen a continuación.
Diseño
Agregue un archivo .swift y .xib cada uno con el mismo nombre a su proyecto. El archivo .xib contiene su diseño de vista personalizado (preferiblemente utilizando restricciones de diseño automático).
Convierta el archivo swift en el propietario del archivo xib.
Código
Agregue el siguiente código al archivo .swift y conecte las salidas y acciones del archivo .xib.
Úsalo
Use su vista personalizada en cualquier parte de su guión gráfico. Simplemente agregue ay
UIView
configure el nombre de la clase a su nombre de clase personalizado.fuente
init(frame:)
. Vea este tutorial para más detalles.Durante un tiempo, el enfoque de Christopher Swasey fue el mejor enfoque que había encontrado. Le pregunté a un par de desarrolladores senior de mi equipo y uno de ellos tenía la solución perfecta . Satisface todas las preocupaciones que Christopher Swasey abordó con tanta elocuencia y no requiere un código de subclase repetitivo (mi principal preocupación con su enfoque). Hay un problema , pero aparte de eso, es bastante intuitivo y fácil de implementar.
MyCustomClass.swift
MyCustomClass.xib
File's Owner
archivo .xib para que sea su clase personalizada (MyCustomClass
)class
valor (debajo deidentity Inspector
) para su vista personalizada en el archivo .xib. Por lo tanto, su vista personalizada no tendrá una clase específica, pero tendrá un Propietario de archivo específico.Assistant Editor
.Connections Inspector
, notará que sus salidas de referencia no hacen referencia a su clase personalizada (es decirMyCustomClass
), sino que hacen referencia a ellaFile's Owner
. ComoFile's Owner
se especifica que es su clase personalizada, las salidas se conectarán y funcionarán correctamente.NibLoadable
protocolo al que se hace referencia a continuación..swift
nombre de su.xib
archivo de clase personalizado es diferente del nombre de su archivo, configure lanibName
propiedad para que sea el nombre de su.xib
archivo.required init?(coder aDecoder: NSCoder)
yoverride init(frame: CGRect)
llamesetupFromNib()
como en el siguiente ejemplo.MyCustomClass
).Aquí está el protocolo al que querrá hacer referencia:
Y aquí hay un ejemplo de
MyCustomClass
que implementa el protocolo (con el nombre del archivo .xibMyCustomClass.xib
):NOTA: Si pierde el Gotcha y establece el
class
valor dentro de su archivo .xib para que sea su clase personalizada, entonces no se dibujará en el guión gráfico y obtendrá unEXC_BAD_ACCESS
error cuando ejecute la aplicación porque se atasca en un bucle infinito de tratando de inicializar la clase desde la punta usando elinit?(coder aDecoder: NSCoder)
método que luego llamaSelf.nib.instantiate
yinit
vuelve a llamar .fuente
setupFromNib()
, parece solucionar ciertos problemas extraños de diseño automático con celdas de vista de tabla de tamaño automático que contienen vistas creadas por XIB.@IBDesignable
compatibilidad. No puedo creer por qué Xcode o UIKit no proporcionan algo como esto por defecto al agregar un archivo UIView.Suponiendo que ha creado una xib que desea usar:
1) Cree una subclase personalizada de UIView (puede ir a Archivo -> Nuevo -> Archivo ... -> Clase de Cocoa Touch. Asegúrese de que "Subclase de:" es "UIView").
2) Agregue una vista basada en xib como una subvista a esta vista en la inicialización.
En Obj-C
En Swift 2
En Swift 3
3) Donde quiera que lo use en su guión gráfico, agregue una vista UIV como lo haría normalmente, seleccione la vista recién agregada, vaya al Inspector de identidad (el tercer icono en la esquina superior derecha que parece un rectángulo con líneas), e ingrese el nombre de su subclase como "Clase" en "Clase personalizada".
fuente
xibView.frame = self.frame;
debería serxibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
, de lo contrario, xibView tendrá un desplazamiento cuando la vista se agregue al guión gráfico.Siempre he encontrado que la solución "agregarlo como una subvista" no es satisfactoria, ya que se atornilla con (1) salida automática, (2)
@IBInspectable
y (3) salidas. En cambio, déjame presentarte la magia deawakeAfter:
unNSObject
método.awakeAfter
le permite cambiar el objeto realmente despertado de un NIB / Storyboard con un objeto completamente diferente. Ese objeto luego se somete al proceso de hidratación, lo invocaawakeFromNib
, se agrega como una vista, etc.Podemos usar esto en una subclase de "recorte de cartón" de nuestra vista, cuyo único propósito será cargar la vista desde el NIB y devolverla para usarla en el Storyboard. La subclase incorporable se especifica en el inspector de identidad de la vista del Guión gráfico, en lugar de la clase original. En realidad, no tiene que ser una subclase para que esto funcione, pero convertirlo en una subclase es lo que le permite a IB ver las propiedades de IBInspectable / IBOutlet.
Esta placa adicional podría parecer subóptima, y en cierto sentido lo es, porque lo ideal
UIStoryboard
sería manejar esto sin problemas, pero tiene la ventaja de dejar el NIB original yUIView
subclase sin modificar por completo. El papel que desempeña es básicamente el de un adaptador o una clase de puente, y es perfectamente válido, en cuanto al diseño, como una clase adicional, incluso si es lamentable. Por otro lado, si prefiere ser parsimonioso con sus clases, la solución de @BenPatch funciona mediante la implementación de un protocolo con algunos otros cambios menores. La cuestión de qué solución es mejor se reduce a una cuestión de estilo de programador: si se prefiere la composición de objetos o la herencia múltiple.Nota: la clase establecida en la vista en el archivo NIB sigue siendo la misma. La subclase integrable solo se usa en el guión gráfico. La subclase no se puede usar para crear una instancia de la vista en código, por lo que no debería tener ninguna lógica adicional. Debe solamente contendrá el
awakeAfter
gancho.⚠️ El único inconveniente importante aquí es que si define restricciones de ancho, alto o relación de aspecto en el guión gráfico que no se relacionan con otra vista, entonces deben copiarse manualmente. Las restricciones que relacionan dos vistas se instalan en el ancestro común más cercano, y las vistas se despiertan desde el guión gráfico desde adentro hacia afuera, por lo que para cuando esas restricciones se hidratan en la supervista, el intercambio ya ha ocurrido. Las restricciones que solo involucran la vista en cuestión se instalan directamente en esa vista y, por lo tanto, se lanzan cuando se produce el intercambio a menos que se copien.
Tenga en cuenta que lo que está sucediendo aquí es que las restricciones instaladas en la vista en el guión gráfico se copian en la vista recién instanciada , que ya puede tener sus propias restricciones, definidas en su archivo nib. Esos no se ven afectados.
instantiateViewFromNib
es una extensión de tipo seguro paraUIView
. Todo lo que hace es recorrer los objetos de la NIB hasta que encuentre uno que coincida con el tipo. Tenga en cuenta que el tipo genérico es el valor de retorno , por lo que el tipo debe especificarse en el sitio de la llamada.fuente
instantiateViewFromNib
no se devuelve nada. No es un gran problema de ninguna manera IMO, la subclase es solo una invención para engancharse en el guión gráfico, todo el código debe estar en la clase original.MyCustomView
. En mi xib faltaba la barra lateral interna izquierda por defecto; para encenderlo, hay un botón al lado del control de rasgos "Ver como: iPhone 7" cerca del lado inferior / izquierdo.La mejor solución actualmente es usar un controlador de vista personalizado con su vista definida en un xib, y simplemente eliminar la propiedad de "vista" que Xcode crea dentro del guión gráfico al agregarle el controlador de vista (no olvide establecer el nombre de aunque la clase personalizada).
Esto hará que el tiempo de ejecución busque automáticamente el xib y lo cargue. Puede usar este truco para cualquier tipo de vista de contenedor o vista de contenido.
fuente
Pienso
alternative
en usarXIB views
para usarView Controller
en un guión gráfico separado .Luego, en guión gráfico principal en lugar de vista personalizada uso
container view
conEmbed Segue
y tienenStoryboardReference
a este controlador de vista personalizada que vista debe ser colocada dentro de otra vista en guión gráfico principal.Luego podemos configurar la delegación y la comunicación entre este ViewController de inserción y el controlador de vista principal a través de la preparación para la segue . Este enfoque es diferente de mostrar UIView, pero se puede utilizar de manera mucho más simple y eficiente (desde la perspectiva de la programación) para lograr el mismo objetivo, es decir, tener una vista personalizada reutilizable que es visible en el guión gráfico principal
La ventaja adicional es que puede implementar su lógica en la clase CustomViewController y configurar toda la delegación y la preparación de la vista sin crear clases de controlador separadas (más difíciles de encontrar en el proyecto) y sin colocar el código repetitivo en el UIViewController principal usando Component. Creo que esto es bueno para componentes reutilizables ej. Componente del reproductor de música (similar a un widget) que se puede insertar en otras vistas.
fuente
Aunque las respuestas más populares funcionan bien, son conceptualmente incorrectas. Todos se usan
File's owner
como conexión entre las salidas de clase y los componentes de la interfaz de usuario.File's owner
se supone que se usa solo para objetos de nivel superior, noUIView
s. Consulte el documento de desarrollador de Apple . Tener UIView comoFile's owner
conduce a estas consecuencias indeseables.contentView
donde se supone que debe usarself
. No solo es feo, sino también estructuralmente incorrecto porque la vista intermedia evita que la estructura de datos transmita su estructura de interfaz de usuario. Es como lo contrario de la IU declarativa.Hay una forma elegante de hacerlo sin usar
File's owner
. Por favor revise esta publicación de blog . Explica cómo hacerlo de la manera correcta.fuente
Aquí está la respuesta que siempre has querido. Puede crear su
CustomView
clase, tener la instancia maestra de la misma en un xib con todas las subvistas y salidas. Luego, puede aplicar esa clase a cualquier instancia en sus guiones gráficos u otras xibs.No es necesario jugar con el Propietario del archivo, o conectar salidas a un proxy o modificar el xib de una manera peculiar, o agregar una instancia de su vista personalizada como una subvista de sí mismo.
Solo haz esto:
UIView
aNibView
(o deUITableViewCell
aNibTableViewCell
)¡Eso es!
Incluso funciona con IBDesignable para referir su vista personalizada (incluidas las subvistas del xib) en tiempo de diseño en el guión gráfico.
Puede leer más sobre esto aquí: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
Y puede obtener el marco de código abierto de BFWControls aquí: https://github.com/BareFeetWare/BFWControls
Y aquí hay un extracto simple del
NibReplaceable
código que lo impulsa, en caso de que tenga curiosidad: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4Tom 👣
fuente
Esta solución puede usarse incluso si su clase no tiene el mismo nombre que el XIB. Por ejemplo, si tiene una clase de controlador de vista base controllerA que tiene un nombre XIB controllerA.xib y lo subclasificó con controllerB y desea crear una instancia de controllerB en un guión gráfico, entonces puede:
* *
fuente
Solución para Objective-C según los pasos descritos en la respuesta de Ben Patch .
Use la extensión para UIView:
Crear archivos
MyView.h
,MyView.m
yMyView.xib
.Primero prepare su
MyView.xib
como la respuesta de Ben Patch dice, así que configure la claseMyView
para el propietario del Archivo en lugar de la vista principal dentro de este XIB.MyView.h
:MyView.m
:Y luego solo crea tu vista programáticamente:
¡Advertencia! La vista previa de esta vista no se mostrará en Storyboard si usa WatchKit Extension debido a este error en Xcode> = 9.2: https://forums.developer.apple.com/thread/95616
fuente