【问题标题】:Load a UIView from nib in Swift在 Swift 中从 nib 加载 UIView
【发布时间】:2014-09-11 12:55:36
【问题描述】:

这是我的 Objective-C 代码,我用它来为我的自定义 UIView 加载一个 nib:

-(id)init{

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
    return [subviewArray objectAtIndex:0];

}

Swift 中的等价代码是什么?

【问题讨论】:

    标签: swift uiview xib


    【解决方案1】:

    如果您的项目中有很多自定义视图,您可以创建像 UIViewFromNib 这样的类

    斯威夫特 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)
        }
    }
    

    斯威夫特 5

    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() {
            let bundle = Bundle(for: UIViewFromNib.self)
            contentView = UINib(nibName: nibName, bundle: bundle).instantiate(withOwner: self).first as? UIView
            contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            contentView.frame = bounds
            addSubview(contentView)
        }
    }
    

    并且在每个类中都继承自UIViewFromNib,如果.xib 文件具有不同的名称,您也可以覆盖nibName 属性:

    class MyCustomClass: UIViewFromNib {
        
    }
    

    【讨论】:

      【解决方案2】:

      更新为Swift 5

      在下面的某个地方定义:

      extension UIView {
          public class func fromNib<T: UIView>() -> T {
              let name = String(describing: Self.self);
              guard let nib = Bundle(for: Self.self).loadNibNamed(
                      name, owner: nil, options: nil)
              else {
                  fatalError("Missing nib-file named: \(name)")
              }
              return nib.first as! T
          }
      }
      

      并像上面一样使用:

      let view = MyCustomView.fromNib();
      

      它将在与MyCustomView 相同的包中搜索,然后加载MyCustomView.nib 文件(如果文件存在,并添加到项目中)。

      【讨论】:

        【解决方案3】:

        // 使用这个类作为超级视图

        import UIKit
        
        class ViewWithXib: UIView {
        
        func initUI() {}
        
        private func xibSetup() {
            let view = loadViewFromNib()
            view.frame = bounds
            view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
            addSubview(view)
            initUI()
        }
        
        private func loadViewFromNib() -> UIView {
            let thisName = String(describing: type(of: self))
            let view = Bundle(for: self.classForCoder).loadNibNamed(thisName, owner: self, options: nil)?.first as! UIView
            return view
        }
        
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            xibSetup()
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            xibSetup()
        }
        
        }
        

        // 用法:

        class HeaderView: ViewWithXib {
        }
        
        
        let header = HeaderView() // No need to load the view from nib, It will work
        

        【讨论】:

          【解决方案4】:

          我就是这样做的:

          if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
              // Do something with myView
          }
          

          此示例使用主包中 nib“MyView.xib”中的第一个视图。但是您可以更改索引、笔尖名称或捆绑包(默认为 main )。

          我曾经将视图唤醒到视图 init 方法中,或者像上面建议的答案那样制作通用方法(顺便说一句,这很聪明),但我不再这样做了,因为我注意到用例通常不同,为了涵盖所有情况,泛型方法变得与使用 UINib.instantiate 方法一样复杂。

          我更喜欢使用工厂对象,通常是使用视图的 ViewController,如果需要在多个地方使用视图,我更喜欢使用专用工厂对象或视图扩展。

          在本例中,ViewController 从 nib 加载视图。 可以更改 nib 文件以对同一视图类使用不同的布局。 (这不是很好的代码,它只是说明了这个想法)

          class MyViewController {
              // Use "MyView-Compact" for compact version
              var myViewNibFileName = "MyView-Standard"
          
              lazy var myView: MyView = {
                  // Be sure the Nib is correct, or it will crash
                  // We don't want to continue with a wrong view anyway, so ! is ok
                  UINib.init(nibName: myViewNibFileName, bundle: nil).instantiate(withOwner: self)[0] as! MyView
              }()
          }
          

          【讨论】:

            【解决方案5】:

            Swift 5 - 简洁易用的扩展

            [从生产项目复制粘贴]

            //
            //  Refactored by Essam Mohamed Fahmi.
            //
            
            import UIKit
            
            extension UIView
            {
               static var nib: UINib
               {
                  return UINib(nibName: "\(self)", bundle: nil)
               }
            
               static func instantiateFromNib() -> Self?
               {
                  return nib.instantiate() as? Self
               }
            }
            
            extension UINib
            {
               func instantiate() -> Any?
               {
                  return instantiate(withOwner: nil, options: nil).first
               }
            }
            

            用法

            let myCustomView: CustomView = .instantiateFromNib()
            

            【讨论】:

              【解决方案6】:

              Swift 4 - 5.1 协议扩展

              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 类同名。前任。我的视图.xib。您可以通过在 MyView 中实现 nibName() 来修改此行为,以返回与默认协议扩展实现不同的名称。

              在 xib 中,文件所有者是 MyView,根视图类是 MyView。

              用法

              let view = MyView.fromNib()
              

              【讨论】:

              • 这是迄今为止最优雅、最直接的解决方案,我不知道为什么它不是公认的答案!
              • @horseshoe7 因为它是在提问 4 年后写的。
              【解决方案7】:

              现在能够快速返回-&gt; 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 输入中输入您的班级

              【讨论】:

              • 我无法让它在 XCode 7.1 beta 3 下工作 - 不确定它是否是 beta 版本,但基本上我已经尝试了各种方法直接从 Swift 中的笔尖创建自定义视图,我总是得到相同的结果:它创建的类与插座不兼容 KVC。不知道是不是我做错了,但我的课很简单,而且文件的所有者是正确的。我曾经在 Objective-C 下一直这样做。
              • @Logan 它与您的代码并不真正相关,但 imo 自定义视图应该支持从 Storyboard/XIB 加载。我的评论只是对那些想要创建此类视图的人的通知
              • 注意,使用第二种调用这个函数的形式,即let myCustomView = UIView.fromNib() as? CustomView,我仍然有问题。在这种情况下,T.self 解析为UIView 而不是CustomView,它无法找到笔尖。我不知道为什么会这样 - 也许let 的推断类型意味着该函数被称为UIView
              • 重要的是要指出,尝试使用 File's Owner 来连接插座(就像我们在过去的美好时光中所做的那样)会导致崩溃。在 IB 中,File's Owner 必须为 nil/empty,并且 outlet 应该连接到视图。
              • @Echelon 你拯救了我的一天!!!我使用 File's Owner 连接了我的插座,但它没有工作,而是使用视图工作。
              【解决方案8】:
                  let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
                  let shareView = nibs![0] as! ShareView
                  self.view.addSubview(shareView)
              

              【讨论】:

                【解决方案9】:

                我更喜欢下面的扩展

                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
                

                【讨论】:

                • 您使用的是什么版本的 Xcode?请确保您使用的是最新版本的 XCode。使用 XCode 11.5(截至日期的最新版本)对我来说效果很好。
                【解决方案10】:
                  let bundle = Bundle(for: type(of: self))
                   let views = bundle.loadNibNamed("template", owner: self, options: nil)
                    self.view.addSubview(views?[0] as! UIView)
                

                【讨论】:

                • 不鼓励仅使用代码的答案。请添加一些解释,说明这如何解决问题,或者这与现有答案有何不同。 From Review
                【解决方案11】:

                我的贡献:

                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()
                

                【讨论】:

                • 迄今为止最好的答案。
                • 最佳答案在这里。干净简单
                • @YuchenZhong - 我更喜欢 [0] 而不是 .first 因为这会返回一个可选的。如果你强行打开它,它不会更安全。 ...这引出了一个问题:为什么不返回一个可选的作为上述解决方案的一些?答:可以。没有错。但是......如果它会返回 nil,则 xib/类的名称不匹配。这是开发人员的错误,应立即发现,切勿将其投入生产。在这里,我宁愿让应用程序崩溃而不是让它处于某种奇怪的状态。只是我的 2 美分/偏好。
                • @allenlinli - 该方法是 UIView 的静态扩展,应该是 CustomView。它之所以有效,是因为编译器使用显式类型注释来推断类型。因为 CustomView 是 UIView 的子类,并且类型已经被推断出来,所以我们不需要再次推断它,因此 UIView 可以省略,如我的第二个示例所示。话虽如此,您显然也可以按照自己喜欢的方式拨打电话。
                • 当 .xib 中有自定义视图时,此解决方案不适用于我。我建议将此部分修复为: return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)![0] as! T
                【解决方案12】:

                使用 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()
                

                【讨论】:

                • 请注意,如果您使用空的 nib 文件或没有 UIView 根节点的 nib 文件执行此操作,您将崩溃,因为您没有检查数组大小或位置 0 的元素。
                【解决方案13】:

                原始解决方案

                1. 我创建了一个 XIB 和一个名为 SomeView 的类(使用相同的名称 方便和可读性)。我都基于 UIView。
                2. 在 XIB 中,我将“文件所有者”类更改为 SomeView(在身份检查器中)。
                3. 我在 SomeView.swift 中创建了一个 UIView 插座,将它链接到 XIB 文件中的顶级视图(为方便起见,将其命名为“视图”)。然后,我根据需要将其他插座添加到 XIB 文件中的其他控件。
                4. 在 SomeView.swift 中,我在“init with code”初始化程序中加载了 XIB。没有必要为“自我”分配任何东西。加载 XIB 后,所有插座都已连接,包括顶层视图。唯一缺少的是将顶视图添加到视图层次结构中:

                .

                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
                    }
                }
                
                1. 使用可丢弃的返回值,因为当所有出口都已连接时,调用者对返回的视图几乎没有兴趣。
                2. 这是一个通用方法,它返回一个 UIView 类型的可选对象。如果加载视图失败,则返回 nil。
                3. 尝试加载与当前类实例同名的 XIB 文件。如果失败,则返回 nil。
                4. 将顶级视图添加到视图层次结构中。
                5. 这一行假设我们使用约束来布局视图。
                6. 此方法添加顶部、底部、前导和尾随约束 - 将视图附加到所有侧面的“自我”(有关详细信息,请参阅:https://stackoverflow.com/a/46279424/2274829
                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 ...
                }
                
                1. SomeClass 是从 SomeClass.xib 文件加载其内容的 UIView 子类。 “final”关键字是可选的。
                2. 在情节提要中使用视图时的初始化程序(请记住使用 SomeClass 作为情节提要视图的自定义类)。
                3. 以编程方式创建视图时的初始化程序(即:“let myView = SomeView()”)。
                4. 使用全零框架,因为此视图使用自动布局进行布局。 请注意,“init(frame: CGRect) {..}”方法不是独立创建的,因为自动布局仅在我们的项目中使用。
                5. & 6. 使用扩展名加载 xib 文件。

                Credit:在此解决方案中使用通用扩展的灵感来自 Robert 在下面的回答。

                编辑 将“view”更改为“contentView”以避免混淆。还将数组下标更改为“.first”。

                【讨论】:

                • 将类名设置为 File's Owner 恰到好处...谢谢!
                • UIView没有属性view,调用self.view会报错
                • @NastyaGorban self.view 在这种情况下实际上是指 GK100 从 .xib 中的顶级视图链接到 SomeView.swift 的插座属性(名为“视图)。不添加该插座将给出你会出错,因为你说的 NSView 类中没有“视图”属性。
                • 我在加载 nib(loadNibNamed) 时崩溃。使用 Xcode 6.3 和 Swift
                • init(coder aDecoder: NSCoder) 内部调用fromNib() 会创建一个无限循环,因为在fromNib() 方法中加载Nib 会调用:init(coder aDecoder: NSCoder)
                【解决方案14】:

                这是一种使用协议和协议扩展 (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()
                

                一些额外的注意事项

                1. 确保您的自定义视图的 xib 文件设置了视图的 Custom Class(以及从那里设置的 outlet/actions),而不是文件所有者的。
                2. 您可以在自定义视图的外部或内部使用此协议/扩展。如果您在初始化视图时还有其他一些设置工作,您可能希望在内部使用它。
                3. 您的自定义视图类和 xib 文件需要具有相同的名称。

                【讨论】:

                  【解决方案15】:

                  斯威夫特 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()
                  

                  【讨论】:

                    【解决方案16】:
                    class func loadFromNib<T: UIView>() -> T {
                        let nibName = String(describing: self)
                        return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      如果您希望 Swift UIView 子类完全自包含,并且能够使用 init 或 init(frame:) 实例化而不暴露使用 Nib 的实现细节,那么您可以使用协议扩展来实现这。此解决方案避免了许多其他解决方案所建议的嵌套 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;
                          }
                      }
                      

                      【讨论】:

                      • 这是一个巧妙的技巧;唯一的缺点是你仍然不能编写一个完全自定义的初始化来设置存储的属性,因为init(coder:) 将在链的下游被调用,这将期望所述属性已经设置或在返回之前自行设置它们到基于协议的初始化。
                      【解决方案18】:

                      最方便的实现。这里需要两个方法,直接返回你类的对象,而不是UIView。

                      1. viewId 标记为,允许覆盖
                      2. 您的.xib 可以包含多个顶层视图,这种情况也是 正确处理。

                      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
                      

                      【讨论】:

                        【解决方案19】:

                        您可以通过情节提要执行此操作,只需为视图添加适当的约束。您可以通过继承您自己的任何视图轻松地做到这一点,比如说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 中放置任何视图并指定类。

                        【讨论】:

                          【解决方案20】:

                          基于洛根回答的更强大的版本

                          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 }
                          }
                          

                          【讨论】:

                            【解决方案21】:

                            您所要做的就是在 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())
                            

                            【讨论】:

                            • 这是一个很好的答案,但有问题,我会编辑它。
                            • 这是我实现的方式。但你可以即兴发挥。提前感谢@C0mrade
                            【解决方案22】:

                            以上述解决方案为基础。

                            这将适用于所有项目包,并且在调用 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")
                            

                            【讨论】:

                              【解决方案23】:

                              Swift 3 版本的 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
                                      }
                                  }
                              }
                              

                              【讨论】:

                                【解决方案24】:

                                与上面的一些答案类似,但更一致的 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/bundle 加载类提供了便利。

                                【讨论】:

                                  【解决方案25】:

                                  在 Xcode 7 beta 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);
                                  
                                  
                                  
                                      }
                                  
                                  
                                  }
                                  

                                  以编程方式访问自定义视图

                                  self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
                                          self.view.addSubview(self.customView!);
                                  

                                  源码-https://github.com/karthikprabhuA/CustomXIBSwift

                                  【讨论】:

                                    【解决方案26】:

                                    我通过以下代码使用 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 view 插座连接到 swift 中定义的 view 插座。您还可以将 First Responder 设置为您的自定义类名称以开始连接任何其他插座。

                                    希望这会有所帮助!

                                    【讨论】:

                                      【解决方案27】:

                                      我更喜欢这个解决方案(基于@GK100的答案):

                                      1. 我创建了一个 XIB 和一个名为 SomeView 的类(为了方便和可读性,使用相同的名称)。我都基于 UIView。
                                      2. 在 XIB 中,我将“文件所有者”类更改为 SomeView(在身份检查器中)。
                                      3. 我在 SomeView.swift 中创建了一个 UIView 插座,将它链接到 XIB 文件中的顶级视图(为方便起见,将其命名为“视图”)。然后,我根据需要将其他插座添加到 XIB 文件中的其他控件。
                                      4. 在 SomeView.swift 中,我在 initinit: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
                                          }
                                        
                                        
                                          ...
                                        }
                                        

                                      【讨论】:

                                      • 我更喜欢将 init 与框架一起使用,所以我将其连根拔起!需要注意的一件事...如果您希望视图与您传入的框架相匹配,请添加 self.view.frame = frame
                                      【解决方案28】:

                                      试试下面的代码。

                                      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 中的 init() 对象初始化方法中调用这个方法。
                                      【解决方案29】:
                                      let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
                                      return subviewArray[0]
                                      

                                      【讨论】:

                                      • 但是在 Swift 的 init() 中,没有返回值。我忘了提到我需要在 UIView 的初始化中调用 loadNibNamed。
                                      • “没有返回值”是什么意思? self 从所有 init 方法中隐式返回...
                                      • 我的意思是我在 init 方法中调用 loadNibNamed。加载的 UIView 在 ObjC 中分配给 self。但很快就不是了。
                                      猜你喜欢
                                      • 2011-02-08
                                      • 1970-01-01
                                      • 2011-05-05
                                      • 2013-04-29
                                      • 2017-10-31
                                      • 1970-01-01
                                      • 2011-03-22
                                      • 2012-10-13
                                      • 1970-01-01
                                      相关资源
                                      最近更新 更多