回答:
元のソリューション
。
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
}
...
}
この方法で、nibから自分自身をロードするクラスを取得することに注意してください。次に、UIViewがプロジェクトで(インターフェイスビルダーまたはプログラムで)使用できるときはいつでも、SomeViewをクラスとして使用できます。
更新-Swift 3構文の使用
次の拡張機能でxibをロードすることはインスタンスメソッドとして記述され、上記のようなイニシャライザによって使用できます。
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
}
}
そして、呼び出し元のメソッドは次のようになります。
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 ...
}
クレジット:このソリューションで一般的な拡張機能を使用することは、以下のRobertの回答に触発されました。
混乱を避けるために、「view」を「contentView」に変更して編集します。また、配列の添え字を「.first」に変更しました。
fromNib()
内からinit(coder aDecoder: NSCoder)
内部ペン先をロードするよう無限ループ作成fromNib()
:メソッドは呼び出しを可能にするinit(coder aDecoder: NSCoder)
私の貢献:
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
次に、次のように呼び出します。
let myCustomView: CustomView = UIView.fromNib()
..あるいは:
let myCustomView: CustomView = .fromNib()
-> Self
迅速に戻ることができるようになったことで、これが少し簡単になります。Swift 5で最後に確認されました。
extension UIView {
class func fromNib(named: String? = nil) -> Self {
let name = named ?? "\(Self.self)"
guard
let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
else { fatalError("missing expected nib named: \(name)") }
guard
/// we're using `first` here because compact map chokes compiler on
/// optimized release, so you can't use two views in one nib if you wanted to
/// and are now looking at this
let view = nib.first as? Self
else { fatalError("view of type \(Self.self) not found in \(nib)") }
return view
}
}
あなたの場合は.xib
、ファイルおよびサブクラスが同じ名前を共有して、あなたが使用することができます。
let view = CustomView.fromNib()
カスタム名がある場合は、次を使用します。
let view = CustomView.fromNib(named: "special-case")
注意:
「タイプYourTypeのビューが見つかりません。」というエラーが発生する場合は、.xib
ファイルにビューのクラスが設定されていません。
.xib
ファイルでビューを選択し、を押しcmd + opt + 4
て、class
入力でクラスを入力します
let myCustomView = UIView.fromNib() as? CustomView
。この場合、ではなくにT.self
解決され、ペン先を見つけることができません。これがなぜなのかわかりません-多分関数が呼び出されたという意味の推測された型?UIView
CustomView
let
UIView
次のコードを試してください。
var uiview :UIView?
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
編集:
import UIKit
class TestObject: NSObject {
var uiview:UIView?
init() {
super.init()
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
}
}
Swift 4プロトコル拡張
public protocol NibInstantiatable {
static func nibName() -> String
}
extension NibInstantiatable {
static func nibName() -> String {
return String(describing: self)
}
}
extension NibInstantiatable where Self: UIView {
static func fromNib() -> Self {
let bundle = Bundle(for: self)
let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)
return nib!.first as! Self
}
}
可決
class MyView: UIView, NibInstantiatable {
}
この実装は、NibがUIViewクラスと同じ名前を持つことを前提としています。例 MyView.xib。MyViewにnibName()を実装してこの動作を変更し、デフォルトのプロトコル拡張実装とは異なる名前を返すことができます。
xibでは、ファイルの所有者はMyViewであり、ルートビュークラスはMyViewです。
使用法
let view = MyView.fromNib()
私は次のコードによってSwiftでこれを達成しました:
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)
}
}
あなたのXIBの接続することを忘れないでくださいビューにコンセントをビュー迅速に定義されているコンセント。First Responderをカスタムクラス名に設定して、追加のコンセントの接続を開始することもできます。
お役に立てれば!
Xcode 7ベータ4、Swift 2.0、iOS9 SDKでテスト済み。次のコードは、xibをuiviewに割り当てます。このカスタムxibビューをストーリーボードで使用して、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);
}
}
プログラムでcustomviewにアクセスする
self.customView = SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
self.view.addSubview(self.customView!);
プロジェクトに多くのカスタムビューがある場合は、次のようなクラスを作成できます。 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)
}
}
スウィフト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)
}
}
また、すべてのクラスでから継承するだけで、ファイルの名前が異なる場合はプロパティUIViewFromNib
をオーバーライドできます。nibName
.xib
class MyCustomClass: UIViewFromNib {
}
上記のソリューションに基づいて構築します。
これはすべてのプロジェクトバンドルで機能し、fromNib()を呼び出すときにジェネリックは必要ありません。
スウィフト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)
}
}
スウィフト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)
}
}
このように使用できます:
let someView = SomeView.fromNib()
またはこのように:
let someView = SomeView.fromNib("SomeOtherNibFileName")
スウィフト4
".first as?CustomView"と書くことを忘れないでください。
if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {
self.view.addSubview(customView)
}
どこでも使いたいなら
最良の解決策は、Robert Gummessonの答えです。
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
次に、次のように呼び出します。
let myCustomView: CustomView = UIView.fromNib()
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]
self
はすべてのinit
メソッドから暗黙的に返されます...
Swiftでこれを行う良い方法は、列挙型を使用することです。
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).first as? UIView
}
}
次に、コードで簡単に使用できます:
let view = Views.view1.getView()
私はこのソリューションを好んでいます(@ GK100の場合の回答に基づく):
SomeView.swiftでは、init
またはinit:frame: CGRect
初期化子内にXIBをロードしました。「自己」に何かを割り当てる必要はありません。XIBがロードされるとすぐに、最上位のビューを含むすべてのアウトレットが接続されます。唯一欠けているのは、トップビューをビュー階層に追加することです。
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
}
...
}
ローガンの回答のSwift 3バージョン
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
}
}
}
以下は、プロトコルとプロトコル拡張(Swift 4.2)を使用してプログラムでビューをロードするクリーンで宣言的な方法です。
protocol XibLoadable {
associatedtype CustomViewType
static func loadFromXib() -> CustomViewType
}
extension XibLoadable where Self: UIView {
static func loadFromXib() -> Self {
let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
// your app should crash if the xib doesn't exist
preconditionFailure("Couldn't load xib for view: \(self)")
}
return customView
}
}
そしてあなたはこれを次のように使うことができます:
// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }
// and when you want to use it
let viewInstance = MyView.loadFromXib()
その他の考慮事項:
Custom Class
、ファイルの所有者ではなく、ビューのセット(およびそこからのアウトレット/アクションセット)があることを確認します。私はこのようにしています:
if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}
このサンプルでは、メインバンドルのnib "MyView.xib"の最初のビューを使用します。ただし、インデックス、nib名、またはバンドル(デフォルトではmain)を変更できます。
私は以前、ビューのinitメソッドにビューを呼び起こしたり、上記のソリューションのようにジェネリックメソッドを作成したりしていました(ちなみにスマートです)が、もうそれはしません。
このようにして、同じビューロジックとコードを維持しながら、さまざまなレイアウトや特性を使用できます。
必要に応じて、ファクトリオブジェクト(通常はビューを使用するviewController)に作成させる方が簡単です。所有者が必要な場合があります(通常、作成されたビューが作成者に接続されたアウトレットを取得した場合)。
それがおそらく、AppleがinitFromNib
UIViewクラスにメソッドを含めなかった理由です...
地上レベルの例をとると、あなたは自分がどのように生まれたかがわかりません。あなたは生まれたばかりです。ビューもそうです;)
UIView
クラスでinitメソッドを呼び出すだけです。
そのようにしてください:
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
}
}
ここで、このビューをビューコントローラーのサブビューとして追加する場合は、ビューコントローラー.swiftファイルでそのようにします。
self.view.addSubview(className())
上記の回答のいくつかに似ていますが、より一貫したSwift3 UIView拡張機能:
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)
}
}
これは、自分で名前を付けたnibからだけでなく、他のnib /バンドルからもクラスをロードできるという便利さを提供します。
ストーリーボードを介してこれを行うことができます。ビューに適切な制約を追加するだけです。あなた自身の例からビューをサブクラス化することでこれを簡単に行うことができますBaseView
:
Objective-C
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
スウィフト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)
}
}
制約を追加する方法を2種類提供します-一般的なものとビジュアルフォーマット言語内-必要なものを選択します:)
また、デフォルトでは、xib
名前は実装クラス名と同じ名前であると想定されていました。いいえの場合-変更するだけxibName
パラメータをするです。
ビューをサブクラス化する場合BaseView
-簡単に任意のビューを配置し、IBでクラスを指定できます。
ローガンの答えに基づくより強力なバージョン
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
}
}
そしてあなたはのように使うことができます
class BaseView: UIView {
override class var nibName: String { return "BaseView" }
weak var delegate: StandardStateViewDelegate?
}
class ChildView: BaseView {
override class var nibIndex: Int { return 1 }
}
最も便利な実装。ここでは、UIViewではなくクラスのオブジェクトに直接戻るために、2つのメソッドが必要です。
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
}
}
例:
guard let customView = CustomView.instance() else { return }
//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true
Swift UIViewサブクラスを完全に自己完結させ、Nibを使用する実装の詳細を公開せずにinitまたはinit(frame :)を使用してインスタンス化する機能が必要な場合は、プロトコル拡張を使用してこれを実現できます。このソリューションは、他の多くのソリューションで提案されているように、ネストされたUIView階層を回避します。
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;
}
}
let bundle = Bundle(for: type(of: self))
let views = bundle.loadNibNamed("template", owner: self, options: nil)
self.view.addSubview(views?[0] as! UIView)
私は以下の拡張子を好む
extension UIView {
class var instanceFromNib: Self {
return Bundle(for: Self.self)
.loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
}
}
この拡張機能と最もよく回答された拡張機能の違いは、定数や変数を保存する必要がないことです。
class TitleView: UIView { }
extension UIView {
class var instanceFromNib: Self {
return Bundle(for: Self.self)
.loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
}
}
self.navigationItem.titleView = TitleView.instanceFromNib
File's Owner
てスポットを当てます...ありがとうございます!