Episode #138

IBDesignable

14 minutes
Published on September 25, 2014

This video is only available to subscribers. Get access to this video and 573 others.

Finally iOS 8 and Xcode 6 are available and out of NDA and we can cover them on NSScreencast. There are tons of new features to cover, so today I'm just going to pick one: IBDesignable. With IBDesignable you can live preview your custom views so you don't have to stare at empty gray boxes in Interface Builder anymore. Interface Builder just got way more useful!

Small Correction: I stated in the video that you must use a framework to get IBDesignable classes to work in Storyboards/interface builder. This was true in earlier betas, but is no longer a requirement.

Episode Links

IBDesignable Poster View

@IBDesignable class PosterView : UIView {

    var label: UILabel!
    var imageView: UIImageView!

    @IBInspectable var text: NSString? {
        didSet {
            label.text = text
        }
    }

    @IBInspectable var image: UIImage? {
        didSet {
            imageView.image = image
        }
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        createSubviews()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        createSubviews()
    }

    func createSubviews() {
        imageView = UIImageView()
        imageView.contentMode = UIViewContentMode.ScaleAspectFill
        addSubview(imageView)

        label = UILabel()
        label.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.6)
        label.textColor = UIColor.whiteColor()
        label.font = UIFont.boldSystemFontOfSize(20)
        label.textAlignment = NSTextAlignment.Center
        addSubview(label)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        layer.borderWidth = 2.0
        layer.borderColor = UIColor.blackColor().CGColor
        layer.cornerRadius = 24
        clipsToBounds = true

        imageView.frame = self.bounds

        let labelHeight: CGFloat = 40
        label.frame = CGRectMake(0, bounds.size.height - labelHeight,
            bounds.size.width, labelHeight)
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()

        text = "Test Title"

    }
}

Adding in Assets that Aren't in Targets

Often you'll want to show default assets on your controls without actually shipping them in your compiled application. To do this, add your assets to the project, but don't add them to any target. To refer to the image path at design-time (without resorting to hard-coded paths) do this:

override func prepareForInterfaceBuilder() {
  super.prepareForInterfaceBuilder()
  let projectPaths = NSProcessInfo.processInfo().environment["IB_PROJECT_SOURCE_DIRECTORIES"].componentsSeparatedByString(",")
  if projectPaths.count > 0 {
    if let projectPath = projectPaths[0] as? String {
      let imagePath = projectPath.stringByAppendingPathComponent("TestImages/MyTestImage.jpg")
      image = UIImage(contentsOfFile: imagePath)
      // do something with image
    }
  }
}