Normalmente me gusta crear y diseñar mis uiviews en el generador de interfaces. A veces necesito crear una sola vista en un xib que se pueda reutilizar en múltiples controladores de vista en un guión gráfico.
Normalmente me gusta crear y diseñar mis uiviews en el generador de interfaces. A veces necesito crear una sola vista en un xib que se pueda reutilizar en múltiples controladores de vista en un guión gráfico.
Respuestas:
Probado con Swift 2.2 y Xcode 7.3.1
// DesignableXibView.swift
import UIKit
@IBDesignable
class DesignableXibView: UIView {
var contentView : UIView?
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
contentView = loadViewFromNib()
// use bounds not frame or it'll be offset
contentView!.frame = bounds
// Make the view stretch with containing view
contentView!.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(contentView!)
}
func loadViewFromNib() -> UIView! {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
Funciona en Xcode 6.3.1
Cree una nueva UIView llamada 'ReuseableView'
Cree un archivo xib coincidente llamado 'ReuseableView'
Establecer el propietario del archivo de la xib
establezca la clase personalizada en 'ReusableView' en el Inspector de identidad.
Haga una salida de la vista en ReuseableView.xib a su interfaz ReuseableView.h
Agregue la implementación initWithCoder para cargar la vista y agregarla como una subvista.
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
// 1. load the interface
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
// 2. add as subview
[self addSubview:self.view];
// 3. allow for autolayout
self.view.translatesAutoresizingMaskIntoConstraints = NO;
// 4. add constraints to span entire view
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];
}
return self;
}
Pruebe su vista reutilizable en un guión gráfico
¡Corre y observa!
UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)
que es más rápida que la versión NSBundle.
@IBDesignable on the class.
tienes que implementarlo init(frame:)
, ¡pero aparte de eso, funciona bastante bien! supereasyapps.com/blog/2014/12/15/…
Actualización de Swift 3 y 4 a la respuesta aceptada
1. Cree una nueva UIView llamada 'DesignableXibView'
2. Cree un archivo xib coincidente llamado 'DesignableXibView'
3. Establecer el propietario del archivo de la xib
Seleccione "DesignableXibView.xib"> "Propietario del archivo"> establezca "Clase personalizada" en 'DesignableXibView' en el Inspector de identidad.
4. Implementación de DesignableXibView
import UIKit
@IBDesignable
class DesignableXibView: UIView {
var contentView : UIView!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
contentView = loadViewFromNib()
// use bounds not frame or it'll be offset
contentView.frame = bounds
// Make the view stretch with containing view
contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
// Adding custom subview on top of our view
addSubview(contentView)
}
func loadViewFromNib() -> UIView! {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
return view
}
}
5 Pruebe su vista reutilizable en un guión gráfico
Abre tu guión gráfico
Agregar una vista
Establecer la clase personalizada de esa vista
DesignableXibView
. Cree el proyecto para ver los cambios.
String(describing: type(of: self))
debería hacer esto de forma segura
La función initWithCoder en swift 2 si alguien tiene problemas para traducirla:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
UINib(nibName: String(self.dynamicType), bundle: NSBundle.mainBundle()).instantiateWithOwner(self, options: nil)
self.addSubview(view)
self.view.translatesAutoresizingMaskIntoConstraints = false
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterY , metrics: nil, views: ["view": self.view]))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterX , metrics: nil, views: ["view": self.view]))
}
Convertir Swift
a Objective-C
no es suficiente para que funcione. He tenido dificultades para permitir la representación en vivo en Storyboard.
Después de traducir todo el código, la vista está bien cargada cuando se ejecuta en el dispositivo (o simulador), pero la representación en vivo en Storyboard no funciona. La razón de esto es que usé [NSBundle mainBundle]
mientras que Interface Builder no tiene acceso a mainBundle. Lo que tienes que usar en su lugar es [NSBundle bundleForClass:self.classForCoder]
. BOOM, ¡la renderización en vivo funciona ahora!
Nota: si tiene problemas con el diseño automático, intente deshabilitarlo Safe Area Layout Guides
en Xib.
Para su comodidad, dejo mi código completo aquí, para que solo tenga que copiar / pegar (para todo el proceso, siga la respuesta original ):
BottomBarView.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface BottomBarView : UIView
@end
BottomBarView.m
#import "BottomBarView.h"
@interface BottomBarView() {
UIView *contentView;
}
@end
@implementation BottomBarView
-(id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self xibSetup];
}
return self;
}
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self xibSetup];
}
return self;
}
-(void) xibSetup {
contentView = [self loadViewFromNib];
contentView.frame = self.bounds;
contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:contentView];
}
-(UIView*) loadViewFromNib {
NSBundle *bundle = [NSBundle bundleForClass:self.classForCoder]; //this is the important line for view to render in IB
UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:bundle];
UIView *view = [nib instantiateWithOwner:self options:nil][0];
return view;
}
@end
Dime si encuentras algunos problemas, pero casi debería funcionar fuera de la caja :)
Si alguien está interesado, aquí está la versión Xamarin.iOS del código Paso 4 de @Garfbargle
public partial class CustomView : UIView
{
public ErrorView(IntPtr handle) : base(handle)
{
}
[Export("awakeFromNib")]
public override void AwakeFromNib()
{
var nibObjects = NSBundle.MainBundle.LoadNib("CustomView", this, null);
var view = (UIView)Runtime.GetNSObject(nibObjects.ValueAt(0));
view.Frame = Bounds;
view.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
AddSubview(rootView);
}
}
Aquí está la respuesta que siempre ha 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 otros xibs.
No es necesario jugar con el propietario del archivo, ni conectar puntos de venta 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
a NibView
(o de UITableViewCell
a NibTableViewCell
)¡Eso es!
Incluso funciona con IBDesignable para representar su vista personalizada (incluidas las subvistas de xib) en el momento del 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 BFWControls aquí: https://github.com/BareFeetWare/BFWControls
Tom 👣