Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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. :(

like image 781
farzadshbfn Avatar asked Jan 09 '16 10:01

farzadshbfn


2 Answers

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
    }
}
like image 168
Collierton Avatar answered Oct 07 '22 19:10

Collierton


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)
    }
}
like image 44
Ikroop Singh Kalsi Avatar answered Oct 07 '22 17:10

Ikroop Singh Kalsi