from - load xib swift 4
Cargue una UIView desde la punta en Swift (20)
Aquí hay una extensión que usa genéricos para cargar una UIView
desde una punta
public extension UIView {
public class func fromNib(nibNameOrNil: String? = nil) -> Self {
return fromNib(nibNameOrNil, type: self)
}
public class func fromNib<T : UIView>(nibNameOrNil: String? = nil, type: T.Type) -> T {
let v: T? = fromNib(nibNameOrNil, type: T.self)
return v!
}
public class func fromNib<T : UIView>(nibNameOrNil: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String
if let nibName = nibNameOrNil {
name = nibName
} else {
// Most nibs are demangled by practice, if not, just declare string explicitly
name = nibName
}
let nibViews = NSBundle.mainBundle().loadNibNamed(name, owner: nil, options: nil)
for v in nibViews {
if let tog = v as? T {
view = tog
}
}
return view
}
public class var nibName: String {
let name = "/(self)".componentsSeparatedByString(".").first ?? ""
return name
}
public class var nib: UINib? {
if let _ = NSBundle.mainBundle().pathForResource(nibName, ofType: "nib") {
return UINib(nibName: nibName, bundle: nil)
} else {
return nil
}
}
}
Yo prefiero esto ya que no requiere ninguna configuración adicional en el plumín. Se basa en convenciones de nomenclatura generales, por lo que si su clase es CustomView
y coincide con una punta llamada: CustomView
, puede hacer esto:
let myCustomView = CustomView.fromNib()
// or if you''re unsure whether or not the nib exists
let myCustomView: CustomView? = CustomView.fromNib()
Si necesita ser específico sobre el nombre de la pluma por cualquier razón, pase una cadena arg:
let myCustomView = MyCustomView.fromNib("non-conventional-name")
Problemas conocidos
Usar esto con una clase de vista privada parece causar problemas. Esto parece ser un problema de todo el sistema.
Aquí está mi código Objective-C que estoy usando para cargar un plumín para mi UIView
personalizada:
-(id)init{
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
return [subviewArray objectAtIndex:0];
}
¿Cuál es el código equivalente en Swift?
Basándose en las soluciones anteriores.
Esto funcionará en todos los paquetes de proyectos y no habrá necesidad de genéricos cuando llame desdeNib ().
Swift 2
extension UIView {
public class func fromNib() -> Self {
return fromNib(nil)
}
public class func fromNib(nibName: String?) -> Self {
func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
let bundle = NSBundle(forClass: T.self)
let name = nibName ?? String(T.self)
return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
}
return fromNibHelper(nibName)
}
}
Swift 3
extension UIView {
public class func fromNib() -> Self {
return fromNib(nibName: nil)
}
public class func fromNib(nibName: String?) -> Self {
func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
let bundle = Bundle(for: T.self)
let name = nibName ?? String(describing: T.self)
return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
}
return fromNibHelper(nibName: nibName)
}
}
Se puede usar así:
let someView = SomeView.fromNib()
O así:
let someView = SomeView.fromNib("SomeOtherNibFileName")
La implementación más conveniente. Aquí necesita dos métodos, para regresar directamente al objeto de su clase, no a UIView.
- viewId marcado como una clase , lo que permite anular
- Su .xib puede contener más de una vista del nivel superior, esta situación también se maneja correctamente.
extension UIView {
class var viewId: String {
return String(describing: self)
}
static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {
return instancePrivate(from: bundle ?? Bundle.main,
nibName: nibName ?? viewId,
owner: owner,
options: options)
}
private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
owner: Any?, options: [AnyHashable : Any]?) -> T? {
guard
let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
let view = views.first(where: { $0 is T }) as? T else { return nil }
return view
}
}
Ejemplo:
guard let customView = CustomView.instance() else { return }
//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true
Logré esto con Swift con el siguiente código:
class Dialog: UIView {
@IBOutlet var view:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = UIScreen.mainScreen().bounds
NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
self.view.frame = UIScreen.mainScreen().bounds
self.addSubview(self.view)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
No se olvide de conectar su toma de vista XIB para ver la salida definida rápidamente. También puede configurar First Responder a su nombre de clase personalizado para comenzar a conectar cualquier punto de venta adicional.
¡Espero que esto ayude!
Mi contribución:
Swift 3 / Swift 4
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
Entonces llámalo así:
let myCustomView: CustomView = UIView.fromNib()
..o incluso:
let myCustomView: CustomView = .fromNib()
Prefiero esta solución (basada en la respuesta si @ GK100):
- Creé un XIB y una clase llamada SomeView (usé el mismo nombre para mayor comodidad y legibilidad). Me basé en un UIView.
- En el XIB, cambié la clase "Propietario del archivo" a SomeView (en el inspector de identidad).
- Creé una salida de UIView en SomeView.swift, vinculándola a la vista de nivel superior en el archivo XIB (llamada "vista" para mayor comodidad). Luego agregué otros puntos de venta a otros controles en el archivo XIB, según sea necesario.
En SomeView.swift, cargué el XIB dentro de
init
oinit:frame: CGRect
Inicializador deinit:frame: CGRect
. No es necesario asignar nada al "yo". Tan pronto como se carga el XIB, todos los enchufes están conectados, incluida la vista de nivel superior. Lo único que falta es agregar la vista superior a la jerarquía de vista:class SomeView: UIView { override init(frame: CGRect) { super.init(frame: frame) NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil) self.addSubview(self.view); // adding the top level view to the view hierarchy } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil) self.addSubview(self.view); // adding the top level view to the view hierarchy } ... }
Probado en Xcode 7 beta 4, Swift 2.0 y iOS9 SDK. El siguiente código asignará xib a la vista. Puede utilizar esta vista xib personalizada en el guión gráfico y también acceder al objeto IBOutlet.
import UIKit
@IBDesignable class SimpleCustomView:UIView
{
var view:UIView!;
@IBOutlet weak var lblTitle: UILabel!
@IBInspectable var lblTitleText : String?
{
get{
return lblTitle.text;
}
set(lblTitleText)
{
lblTitle.text = lblTitleText!;
}
}
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib ()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib ()
}
func loadViewFromNib() {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);
}
}
Acceda a la vista personalizada programáticamente
self.customView = SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
self.view.addSubview(self.customView!);
Código fuente - https://github.com/karthikprabhuA/CustomXIBSwift
Puedes hacerlo a través del guión gráfico, solo agrega las restricciones adecuadas para ver. Puede hacer esto fácilmente subclasificando cualquier vista de su propia, digamos BaseView
:
C objetivo
BaseView.h
/*!
@class BaseView
@discussion Base View for getting view from xibFile
@availability ios7 and later
*/
@interface BaseView : UIView
@end
BaseView.m
#import "BaseView.h"
@implementation BaseView
#pragma mark - Public
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self prepareView];
}
return self;
}
#pragma mark - LifeCycle
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self prepareView];
}
return self;
}
#pragma mark - Private
- (void)prepareView
{
NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
UIView *view = [nibsArray firstObject];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
[self addConstraintsForView:view];
}
#pragma mark - Add constraints
- (void)addConstraintsForView:(UIView *)view
{
[self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0]
]];
}
@end
Swift 4
import UIKit
class BaseView : UIView {
// MARK: - LifeCycle
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepareView()
}
override init(frame: CGRect) {
super.init(frame: frame)
prepareView()
}
internal class func xibName() -> String {
return String(describing: self)
}
// MARK: - Private
fileprivate func prepareView() {
let nameForXib = BaseView.xibName()
let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
if let view = nibs?.first as? UIView {
view.backgroundColor = UIColor.clear
view.translatesAutoresizingMaskIntoConstraints = false
addSubviewWithConstraints(view, offset: false)
}
}
}
UIView+Subview
public extension UIView {
// MARK: - UIView+Extensions
public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
subview.translatesAutoresizingMaskIntoConstraints = false
let views = [
"subview" : subview
]
addSubview(subview)
var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
NSLayoutConstraint.activate(constraints)
}
}
Proporciono 2 variantes de cómo agregar restricciones, una común y en el lenguaje de formato visual, seleccione las que desee :)
Además, de forma predeterminada se supone que el nombre xib
tiene el mismo nombre que el nombre de la clase de implementación. Si no, simplemente cambie el parámetro xibName
.
Si subclasifica su vista desde BaseView
, puede colocar fácilmente cualquier vista y especificar la clase en IB.
Si desea que la subclase Swift UIView sea completamente independiente, y tenga la capacidad de crear instancias usando init o init (frame :) sin exponer los detalles de implementación del uso de un Nib, entonces puede usar una extensión de protocolo para lograr esto. Esta solución evita la jerarquía anidada de UIView tal como lo sugieren muchas de las otras soluciones.
public class CustomView: UIView {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var valueLabel: UILabel!
public convenience init() {
self.init(frame: CGRect.zero)
}
public override convenience init(frame: CGRect) {
self.init(internal: nil)
self.frame = frame
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
fileprivate func commonInit() {
}
}
fileprivate protocol _CustomView {
}
extension CustomView: _CustomView {
}
fileprivate extension _CustomView {
// Protocol extension initializer - has the ability to assign to self, unlike
// class initializers. Note that the name of this initializer can be anything
// you like, here we''ve called it init(internal:)
init(internal: Int?) {
self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
}
}
Si tiene muchas vistas personalizadas en su proyecto, puede crear clases como UIViewFromNib
Swift 2.3
class UIViewFromNib: UIView {
var contentView: UIView!
var nibName: String {
return String(self.dynamicType)
}
//MARK:
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
//MARK:
private func loadViewFromNib() {
contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
contentView.frame = bounds
addSubview(contentView)
}
}
Swift 3
class UIViewFromNib: UIView {
var contentView: UIView!
var nibName: String {
return String(describing: type(of: self))
}
//MARK:
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
//MARK:
func loadViewFromNib() {
contentView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?[0] as! UIView
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contentView.frame = bounds
addSubview(contentView)
}
}
Y en cada clase solo hereda de UIViewFromNib
, también puede anular la propiedad .xib
si el archivo .xib
tiene un nombre diferente:
class MyCustomClass: UIViewFromNib {
}
Similar a algunas de las respuestas anteriores, pero una extensión Swift3 UIView más consistente:
extension UIView {
class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
let bundle = bundle ?? Bundle.main
let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
return nibViews?.first as? A
}
class func fromNib<T: UIView>() -> T? {
return fromNib(nibName: String(describing: T.self), bundle: nil)
}
}
Lo que le da la comodidad de poder cargar la clase desde una plumilla propia pero también desde otras plumillas / paquetes.
Solo lo hago de esta manera:
if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}
Esta muestra utiliza la primera vista en la punta "MyView.xib" en el paquete principal. Pero puede variar el índice, el nombre de la lista o el paquete (principal por defecto).
Solía despertar vistas en el método view init o hacer métodos genéricos como en las soluciones anteriores (que son inteligentes por cierto), pero ya no lo hago.
De esta forma puedo usar diferentes diseños o rasgos mientras mantengo la misma lógica de vista y código.
Me resulta más fácil dejar que un objeto de fábrica (generalmente el viewController que usará la vista) lo cree como lo necesite. A veces necesitas un propietario (generalmente cuando la vista creada tiene una salida conectada al creador), a veces no ...
Esa es probablemente la razón por la que Apple no incluyó un método initFromNib
en su clase UIView ...
Para tomar un ejemplo a nivel del suelo, no sabes cómo naces. Usted acaba de nacer. También lo son las vistas;)
Todo lo que tienes que hacer es llamar al método init en tu clase UIView
.
Hazlo de esa manera:
class className: UIView {
@IBOutlet var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
func setup() {
UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
addSubview(view)
view.frame = self.bounds
}
}
Ahora, si desea agregar esta vista como una vista secundaria en el controlador de vista, hágalo de esa manera en el archivo de la vista controller.swift:
self.view.addSubview(className())
Una buena manera de hacer esto con Swift es usar una enumeración.
enum Views: String {
case view1 = "View1" // Change View1 to be the name of your nib
case view2 = "View2" // Change View2 to be the name of another nib
func getView() -> UIView {
return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil)[0] as! UIView
}
}
Luego, en su código, simplemente puede usar:
let view = Views.view1.getView()
Versión más potente basada en la respuesta de Logan
extension UIView {
public class func fromNib(nibName: String? = nil) -> Self {
return fromNib(nibName: nibName, type: self)
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
return fromNib(nibName: nibName, type: T.self)!
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String
if let nibName = nibName {
name = nibName
} else {
name = self.nibName
}
if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
view = tog
}
}
return view
}
public class var nibName: String {
return "/(self)".components(separatedBy: ".").first ?? ""
}
public class var nibIndex: Int {
return 0
}
public class var nibBundle: Bundle {
return Bundle.main
}
}
Y puedes usar como
class BaseView: UIView {
override class var nibName: String { return "BaseView" }
weak var delegate: StandardStateViewDelegate?
}
class ChildView: BaseView {
override class var nibIndex: Int { return 1 }
}
intenta seguir el código.
var uiview :UIView?
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
Editar:
import UIKit
class TestObject: NSObject {
var uiview:UIView?
init() {
super.init()
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
}
}
Solución Orignal
- Creé un XIB y una clase llamada SomeView (usé el mismo nombre para mayor comodidad y legibilidad). Me basé en un UIView.
- En el XIB, cambié la clase "Propietario del archivo" a SomeView (en el inspector de identidad).
- Creé una salida de UIView en SomeView.swift, vinculándola a la vista de nivel superior en el archivo XIB (llamada "vista" para mayor comodidad). Luego agregué otros puntos de venta a otros controles en el archivo XIB, según sea necesario.
- en SomeView.swift, cargué el XIB dentro del inicializador "init with code". No es necesario asignar nada al "yo". Tan pronto como se carga el XIB, todos los enchufes están conectados, incluida la vista de nivel superior. Lo único que falta es agregar la vista superior a la jerarquía de vista:
.
class SomeView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
self.addSubview(self.view); // adding the top level view to the view hierarchy
}
...
}
Tenga en cuenta que de esta manera obtengo una clase que se carga desde el plumín. Podría usar SomeView como clase siempre que UIView pueda usarse en el proyecto (en el constructor de interfaces o programáticamente).
Actualización: utilizando la sintaxis de Swift 3
La carga de un xib en la siguiente extensión se escribe como un método de instancia, que luego puede ser utilizado por un inicializador como el anterior:
extension UIView {
@discardableResult // 1
func fromNib<T : UIView>() -> T? { // 2
guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else { // 3
// xib not loaded, or its top view is of the wrong type
return nil
}
self.addSubview(contentView) // 4
contentView.translatesAutoresizingMaskIntoConstraints = false // 5
contentView.layoutAttachAll(to: self) // 6
return contentView // 7
}
}
- Usar un valor de retorno descartable ya que la vista devuelta no tiene mayor interés para la persona que llama cuando todas las salidas ya están conectadas.
- Este es un método genérico que devuelve un objeto opcional de tipo UIView. Si no puede cargar la vista, devuelve nil.
- Intentando cargar un archivo XIB con el mismo nombre que la instancia de clase actual. Si eso falla, se devuelve nil.
- Agregar la vista de nivel superior a la jerarquía de vista.
- Esta línea asume que estamos usando restricciones para diseñar la vista.
- Este método agrega restricciones superior, inferior, inicial y posterior: adjuntando la vista a "auto" en todos los lados (Ver: https://.com/a/46279424/2274829 para más detalles)
- Devolución de la vista de nivel superior
Y el método de llamada podría ser así:
final class SomeView: UIView { // 1.
required init?(coder aDecoder: NSCoder) { // 2 - storyboard initializer
super.init(coder: aDecoder)
fromNib() // 5.
}
init() { // 3 - programmatic initializer
super.init(frame: CGRect.zero) // 4.
fromNib() // 6.
}
// other methods ...
}
- SomeClass es una subclase UIView que carga su contenido de un archivo SomeClass.xib. La palabra clave "final" es opcional.
- Un inicializador para cuando se usa la vista en un guión gráfico (recuerde utilizar SomeClass como la clase personalizada de su vista del guión gráfico).
- Un inicializador para cuando la vista se crea programáticamente (es decir: "let myView = SomeView ()").
- Usando un marco de todos los ceros ya que esta vista se presenta usando el diseño automático. Tenga en cuenta que el método "init (frame: CGRect) {..}" no se crea de forma independiente, ya que el diseño automático se utiliza exclusivamente en nuestro proyecto.
- & 6. Cargando el archivo xib usando la extensión.
Crédito: El uso de una extensión genérica en esta solución se inspiró en la respuesta de Robert a continuación.
Editar Cambiar "ver" a "contentView" para evitar confusiones. También cambió el subíndice de matriz a ".first".
Swift 3 versión de la respuesta de Logan
extension UIView {
public class func fromNib(nibName: String? = nil) -> Self {
return fromNib(nibName: nibName, type: self)
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
return fromNib(nibName: nibName, type: T.self)!
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String
if let nibName = nibName {
name = nibName
} else {
name = self.nibName
}
if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
for nibView in nibViews {
if let tog = nibView as? T {
view = tog
}
}
}
return view
}
public class var nibName: String {
return "/(self)".components(separatedBy: ".").first ?? ""
}
public class var nib: UINib? {
if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
return UINib(nibName: nibName, bundle: nil)
} else {
return nil
}
}
}
class func loadFromNib<T: UIView>() -> T {
let nibName = String(describing: self)
return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]