6

I know how to use @IBDesignable with custom views. but is it possible to use IBDesignable for cells and render them in storyboard?

for example: i have a collectionViewController in storyboard, and added a uiCollectionCell and specified class as my customCellClass.

p.s: i know for using Xibs in collecionViews and tableViews we have to call method registerNib:forReuseIdentifer in code (and i am doing it). just wondered, is it possible to see it's rendered view in storyboard or not.

p.s2: i found this and it works perfectly with UIViews, but don't know how to make it work with CollectionCells and TableCells. :(

farzadshbfn
  • 2,710
  • 1
  • 18
  • 38
  • seems solved now : https://github.com/mbogh/NibDesignable/commit/aafff304fbfa01a53b3a842c23c2b3cd2d724e8c – Diwann Feb 22 '16 at 07:36
  • Yeah, i figured you should not add TableViewCell or CollectionViewCell in nib files. you just have to add a simple view. and it's being added to the contentView. – farzadshbfn Feb 23 '16 at 06:18
  • I add the collection view by code within my Custom UIView then also it's not rendering in the storyboard. Any idea? – Soumen Jun 04 '18 at 08:20
  • I think for collectionView to render in storyboard, you have to do something in ‘prepareDorInterfaceBuilder’ method. @Soumen – farzadshbfn Jun 09 '18 at 05:32
  • I tried many ways, I think as the Collection or Table has a dynamic cell type it's not possible to render the collection view with its cell. I find collection view is rendering until and unless Datasource is connected with the view. – Soumen Jun 10 '18 at 09:06
  • @Soumen Exactly, that's what I meant, in `prepareForInterfaceBuilder` you can get an instance of your cell, and add it's view to collectionView by hand. (i guess it should be possible). Otherwise, I don't think IB supports it. – farzadshbfn Jun 10 '18 at 14:31

3 Answers3

1

Yes. Here is what I found with Xcode 10.1 and iOS 12. Adding @IBDesignable to the custom subclass of UICollectionViewCell did work intermittently, but this works more reliably:

  • Add @IBDesignable to a custom subclass of UIView
  • Override layoutSubviews(), and define the appearance there
  • Optionally, if you want to define dummy data for IB only, override prepareForInterfaceBuilder()
  • Add that custom view to your prototype UICollectionViewCell in Interface Builder
  • You should see Interface Builder "build" the views and draw your change in your customer view (I find this unreliable, too. If nothing happens and Menu / Editor / Automatically Refresh Views is checked, make some other change in Interface Builder)

Example 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
    }
}
Collierton
  • 539
  • 5
  • 10
1

Yep. Here's how I did it.

  1. First make sure the File Owner of NIB file is set to your custom cell class. Check this

  2. Override the prepareForInterfaceBuilder method and add the contentView from NIB file in the contentView of prototype cell. This is what it looks like.

// 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. Now set the custom class of the prototype cell in your table view to the one above and refresh the views.

Editor > Refresh All Views

If it still does not show up. Just clear the build folder and refresh all views again.

Product > Clean Build Folder

I made a handy extension for myself to reuse the last 2 functions in all 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)
    }
}
ikroop
  • 96
  • 1
  • 6
-2

After lots of testing and working with the library I came up with this:

you should not add TableViewCell or CollectionViewCells inside .nib files, instead you have to add simple View. I'm not sure if it's gonna show up inside storyboard or not (haven't checked it yet) but it makes errors go away. Now you can even use autoLayout for self sizing cells.

CAFEBABE
  • 3,983
  • 1
  • 19
  • 38
farzadshbfn
  • 2,710
  • 1
  • 18
  • 38