是否可以将 IBDesignable 与 UICollectionViewCells 一起使用并在情节提要中实时渲染它们?

Is it possible to use IBDesignable with UICollectionViewCells and live render them in storyboard?

我知道如何将@IBDesignable 与自定义视图一起使用。 但是是否可以对单元格使用 IBDesignable 并在故事板中渲染它们?

例如:我在故事板中有一个 collectionViewController,并添加了一个 uiCollectionCell 并指定 class 作为我的 customCellClass。

p.s:我知道在 collecionViews 和 tableViews 中使用 Xibs 我们必须在代码中调用方法 registerNib:forReuseIdentifer(我正在这样做)。只是想知道,是否可以在情节提要中看到它的渲染视图。

p.s2:我发现 this 并且它与 UIViews 完美配合,但不知道如何使其与 CollectionCells 和 TableCells 配合使用。 :(

经过大量测试和使用库后,我想到了这个:

您不应在 .nib 文件中添加 TableViewCellCollectionViewCells,而必须添加简单的视图。我不确定它是否会出现在情节提要中(尚未检查),但它会消除错误。现在您甚至可以使用 autoLayout 来调整单元格的大小。

是的。这是我在 Xcode 10.1 和 iOS 12 中发现的。将 @IBDesignable 添加到 UICollectionViewCell 的自定义子类确实间歇性地工作,但这更可靠:

  • @IBDesignable 添加到 UIView
  • 的自定义子类
  • 覆盖 layoutSubviews(),并在那里定义外观
  • 可选,如果您只想为 IB 定义虚拟数据,请覆盖 prepareForInterfaceBuilder()
  • 在 Interface Builder
  • 中将自定义视图添加到您的原型 UICollectionViewCell
  • 您应该查看 Interface Builder "build" 视图并在您的客户视图中绘制您的更改(我发现这也不可靠。如果没有任何反应并且 Menu / Editor / Automatically Refresh Views 被选中,请在界面生成器)

例子Class

@IBDesignable
class Avatar: UIView {

    // Despite not being used for views designed in Interface Builder, must still be defined for custom UIView subclasses with @IBDesignable, or IB will report errors
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    // Used when a view is designed inside a view controller scene in Interface Builder and assigned to this custom UIView subclass
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        self.layer.cornerRadius = self.bounds.width / 2
        self.backgroundColor = UIColor.gray
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()

        self.backgroundColor = UIColor.blue
    }
}

是的。这是我的做法。

  1. 首先确保 NIB 文件的文件所有者设置为您的自定义单元格 class。检查 this

  2. 覆盖 prepareForInterfaceBuilder 方法并在原型单元格的 contentView 中添加来自 NIB 文件的 contentView。这是它的样子。

// ArticleTableViewCell.swift

import UIKit

@IBDesignable
class ArticleTableViewCell: UITableViewCell {    
    @IBOutlet weak var authorLabel: UILabel!
    @IBOutlet weak var authorImage: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var dateCreatedLabel: UILabel!

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        addNIBContentView(toView: contentView)
    }

    private func addNIBContentView() {
        let view = loadContentViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.addSubview(view)        
    }

    private func loadContentViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        // Make sure your NIB file is named the same as this class, or else 
        // Put the name of NIB file manually (without the file extension)
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
        return view
    }
}

  1. 现在将 table 视图中原型单元格的自定义 class 设置为上面的那个并刷新视图。

Editor > Refresh All Views

如果还是没有出现。只需清除构建文件夹并再次刷新所有视图。

Product > Clean Build Folder

我为自己做了一个方便的扩展,以重用所有 UITableViews/UICollectionViews

中的最后两个函数
// ViewExtensions.swift

import UIKit

extension UIView {
    func addNIBContentView(toView contentView: UIView? = nil) {
        let view = loadContentViewFromNib()
        // Use bounds not frame or it'll be offset
        view.frame = bounds
        // Make the view stretch with containing view
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        if let contentView = contentView {
            contentView.addSubview(view)
        } else {
            addSubview(view)
        }
    }

    private func loadContentViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        // Make sure your NIB file is named the same as it's class
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
        return view
    }
}
// ArticleTableViewCell.swift

import UIKit

@IBDesignable
class ArticleTableViewCell: UITableViewCell {    
    @IBOutlet weak var authorLabel: UILabel!
    @IBOutlet weak var authorImage: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var dateCreatedLabel: UILabel!

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        addNIBContentView(toView: contentView)
    }
}