【问题标题】:Assign xib to the UIView in Swift将 xib 分配给 Swift 中的 UIView
【发布时间】:2014-08-13 17:41:59
【问题描述】:

在目标c中,它可以在init方法中通过

-(id)init{
    self = [[[NSBundle mainBundle] loadNibNamed:@"ViewBtnWishList" owner:0 options:nil]     objectAtIndex:0];
return self;
}

但是当我迅速这样做时

init(frame: CGRect) {
    self = NSBundle.mainBundle().loadNibNamed("ViewDetailMenu", owner: 0, options: nil)[0] as? UIView
}

在方法错误中无法分配给自己。 现在我的方法是创建一个视图,并将从 nib 加载的视图添加到其中。 有人有更好的主意吗?

【问题讨论】:

  • 使用UINib 会更有效率...无论如何,self 不能在 Swift 中分配。您可能想创建一个类方法,该方法使用 xib 文件初始化您的类。

标签: uiview swift xib


【解决方案1】:

适用于 Swift 4

extension UIView {
    class func loadFromNibNamed(nibNamed: String, bundle: Bundle? = nil) -> UIView? {
      return UINib(
          nibName: nibNamed,
          bundle: bundle
      ).instantiate(withOwner: nil, options: nil)[0] as? UIView
  }
}

适用于 Swift 3

你可以在 UIView 上创建一个扩展:

extension UIView {
    class func loadFromNibNamed(nibNamed: String, bundle: NSBundle? = nil) -> UIView? {
        return UINib(
            nibName: nibNamed,
            bundle: bundle
        ).instantiateWithOwner(nil, options: nil)[0] as? UIView
    }
}

注意:使用 UINib 更快,因为它会为您进行缓存。

那么你可以这样做:

ViewDetailItem.loadFromNibNamed("ViewBtnWishList")

您将能够在任何视图上重用该方法。

【讨论】:

  • 你为什么要使用 3-4 倍 slower NSBundle 来加载笔尖?
  • @holex 我只是想让代码保持一致以解决实际问题,但我更新了它。
  • 谢谢,所以我可以创建自定义 uiview 如下?? var customView = ViewDetailItem.loadFromNibNamed("ViewBtnWishList") 但是如果我想在创建自定义视图时初始化更多变量怎么办?在这种情况下,我们是否只是绕过了 UIView 的 init 方法?
  • 如果我在包含其他视图的布局中使用它会遇到问题,然后我向班级发出出口,我得到Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x17000bae0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key infoView.'
  • 只是好奇 - 为什么使用类 func 而不是静态 func
【解决方案2】:

这对我有用。

override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? {
    if self.subviews.count == 0 {
        return loadNib()
    }
    return self
}

private func loadNib() -> YourCustomView {
    return NSBundle.mainBundle().loadNibNamed("YourCustomViewNibName", owner: nil, options: nil)[0] as YourCustomView
}

【讨论】:

  • 这是在情节提要或 xib 文件中重用的最佳解决方案。如果您只是将加载的 nib 视图添加为子视图,则无法正确设置委托等属性
  • 确保如果您有任何插座,请将它们连接到您的 XIB 中的视图,而不是 File Owner,否则您将收到 KVO 合规性错误。
  • 这是迄今为止唯一适合我的。谢谢!
  • 无论如何都是最合适的解决方案。我不知道为什么在我找到它之前只有 10 票赞成。
  • 但不幸的是,在使用情节提要时,此解决方案确实替换了情节提要中最初的对象,这导致您的视图约束或情节提要完成的任何其他设置消失的情况。
【解决方案3】:

在 Xcode 7 beta 4 和 Swift 2.0 中测试。 以下代码将 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

【讨论】:

  • 也可以在其他 XIB 中使用 - 这很棒。但是 - 一个问题,这不是直接从 XIB 创建自定义视图。它正在从 XIB 加载 UIView 并将其作为子视图添加到自定义视图中。
  • 最出色的贡献。非常清晰和完整。也感谢您在 Github 存储库中发布它!一个小问题是SimpleCustomView.swift 中的成员变量“view”似乎是不必要且未使用的。
  • 许多解决方案中的一个,不是从静态方法设置视图)
【解决方案4】:

这可能是您的解决方案:

斯威夫特 3.x

class func instanceFromNib() -> UIView {
    return UINib(nibName: "<<NibFileName>>", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}

斯威夫特 2.x

class func instanceFromNib() -> UIView {
    return UINib(nibName: "<<NibFileName>>", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
}

【讨论】:

  • 完美答案。简短,简洁,像魅力一样工作。谢谢!
  • 那么我会将这段代码放在 XIB 的视图控制器中还是我试图将 XIB 添加到的视图控制器中?
  • @MarksCode,你需要把这个sn-p加入UIView类的子集;在这种情况下,这与任何视图控制器无关。
【解决方案5】:

我认为这是将 xib 分配给 UIView 的最简单但也是最简洁的方法。 Xcode 7.3 和 Swift 2.0。

import UIKit

//Create CustomView class
class CustomView: UIView {

    class func instanceFromNib() -> UIView {
        return UINib(nibName: "CustomView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
    }
}

//Use it
let customView = CustomView.instanceFromNib() as! CustomView

【讨论】:

  • edit 提供更多信息。纯代码和“试试这个”的答案是discouraged,因为它们不包含可搜索的内容,也没有解释为什么有人应该“试试这个”。我们在这里努力成为知识的资源。
【解决方案6】:

真正的 Swift 方法是使用协议和协议扩展。

我这样使用它:首先我创建一个协议

    protocol XibInitializable {
        static var name: String { get }
        static var bundle: Bundle? { get }

        static func fromXib() -> Self
    }

然后我做这个协议的默认实现使用协议扩展

extension XibInitializable where Self : UIView {
    static var name: String {
        return String(describing: Self.self)
    }

    static var bundle: Bundle? {
        return nil
    }

    static func fromXib() -> Self {
        return UINib(nibName: name, bundle: bundle).instantiate(withOwner: nil, options: nil)[0] as! Self
    }
}

我们的协议的实现现在已经完成

为了让这个协议工作,你需要我们的 xib 文件的名称和类是相同的。例如,例如

最后添加协议并使您的类“final”,就像这里一样。

就是这样

并使用

【讨论】:

  • 我唯一要更改的是将bundle 属性作为默认为nil 的参数移动到函数中。我也会使用nibName 而不是name
【解决方案7】:

您可以定义协议并将实现添加到协议扩展,而不是向 UIView 添加扩展。然后,您可以声明 UIView 符合协议。

这允许返回类型为Self 而不是UIView。所以调用者不必强制转换为类。

这里解释: https://stackoverflow.com/a/33424509/845027

import UIKit

protocol UIViewLoading {}
extension UIView : UIViewLoading {}

extension UIViewLoading where Self : UIView {

  // note that this method returns an instance of type `Self`, rather than UIView
  static func loadFromNib() -> Self {
    let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiateWithOwner(self, options: nil).first as! Self
  }

}

【讨论】:

    【解决方案8】:

    刚刚做了一个UINib 扩展来从xib 加载视图并使用约束、使用泛型和强命名嵌入到容器视图中(不使用字符串,假设xib 和实现具有相同的文件名):

    extension UINib {
    
        static func instantiateViewAndEmbedWithConstraints <T: UIView> (viewType viewType: T.Type, embedInto containerView: UIView) -> T {
            let view = UINib(nibName: String(viewType), bundle: nil).instantiateWithOwner(nil, options: nil).first as! T
            containerView.addSubview(view)
            view.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: containerView, attribute: .Leading, multiplier: 1, constant: 0).active = true
            NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: containerView, attribute: .Trailing, multiplier: 1, constant: 0).active = true
            NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: containerView, attribute: .Top, multiplier: 1, constant: 0).active = true
            NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal, toItem: containerView, attribute: .Bottom, multiplier: 1, constant: 0).active = true
            return view
        }
    
    }
    

    用法:

    ...outlets...
    @IBOutlet var containerView: UIView!
    var customView: CustomView!
    
    ...viewDidLoad...
    customView = UINib.instantiateViewAndEmbedWithConstraints(viewType: CustomView.self, embedInto: containerView)
    

    【讨论】:

      【解决方案9】:

      只需继承这个简单的类(swift 5):

      open class NibView: UIView {
      
          open override func awakeAfter(using coder: NSCoder) -> Any? {
              if subviews.count == 0 {
                  return UINib(nibName: "\(Self.self)", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
              }
              return self
          }
      }
      
      class CustomView: NibView {
      }
      

      正如其他人指出的那样,将 File's Owner 设置为您的 CustomView 类(而不是 xib 的根视图本身)。然后将自定义类设置为CustomView 到您希望被自定义视图类替换的任何视图。此外,自动布局尊重 xib 中的所有约束,至少作为 UITableViewCell 内容视图的子视图。不确定其他情况。

      虽然很简单,但 Apple 却以某种方式为我们完成了这样一个基本的任务!多么棒的公司啊!永远不会对他们感到无聊!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-02-29
        • 2016-06-10
        • 2018-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多