Create an IBDesignable UIView subclass with code from an XIB file in Xcode 6

Many times, I find myself wanting the visual power of both design and layout from Xcode 6’s new interface builder. I’m a visual person, and sometimes I like to create custom UIControl or custom UIView objects that can be reused.


You can create a custom UIView subclass and load the interface from an XIB file. In Xcode 6, you can also go a step further and provide design time visualization and customization in Xcode’s Attributes Inspector.

This is a big deal if you’ve ever loaded an XIB file from code and attached it to a user interface. In the past, you could block out the colors of the UI, but you could never see the composition. You also couldn’t change any of the parameters at design time within your main Storyboard or XIB file.

Custom UIView Subclass (e.g.: CustomView)

These are the steps to making a custom UIView that is designable within Xcode:

  1. Create a View .xib file (e.g.: CustomView.xib)

  2. Design the user interface in Xcode

  3. Set up Auto Layout constraints

  4. Create a Swift code file (CustomView.swift)

  5. Set .xib file’s “File’s Owner” custom class to CustomView (it must match the class name)

  6. Implement both CustomView initializers: init(coder:) and init(frame:)

  7. Load the UIView from the .xib file using the NSBundle and UINib classes

  8. Add the view as a subview and property for the class (future modifications)

  9. Add autoresizing masks for the view to match the size of the CustomView itself

  10. Make the view’s frame leverage the design time size of the embedded CustomView’s bounds

Note

If you leverage any image assets, you’ll want to make sure they are in the same bundle as the custom view. This is especially important when trying to leverage extensions or static libraries.

Visual Guide for a custom UIView from an XIB file

In the following steps I will visually show and explain what you'll need to do to create an IBDesignable and IBInspectable UIView that you can reuse in your iPhone apps. You will be able to leverage the power of design time editing in Xcode along with a better visualization of what your app will look like.

1. Create a View XIB file (e.g.: CustomView.xib)

The first thing you'll need is a new user interface file called an XIB or .xib or nib file. This is like a storyboard file, but it only holds a single UIView object.

Create a new file from the Menu bar using File > New > File or the keyboard shortcut Command + N:

Select iOS > User Interface > View; name it “CustomView” and add it to your target:

2. Setup the UIView for a custom size and disable the status bar.

The UIView will initially be setup for a full-screen iPhone view with a standard size and a status bar. You'll need to make some adjustments to use a smaller size.

Set the Size to “Freeform” and the Status Bar to “None:”

3. Design the UI for your custom control or widget

Leverage the power of Xcode to drag and drop the UI elements for your custom widget. Drag out buttons, labels, image views, and anything else that you want to display in the custom view.

4. Connect Auto Layout constraints to control layout

Depending on how your control is going to be used, you will want to setup constraints that either keeps the size fixed or make the control adaptive to larger interfaces.

You can select all of the UI elements to see if the connections are good. Blue = good, orange = missing constraints, and red = conflicts. Using constraints you need to establish the rules to set the width, height, and position of every UI element.

Auto Layout enables the relative layout of visual elements in your iOS app:

5. Create a Swift code file to drive the custom UIView or UIControl

You need to connect the XIB file to a Swift code file in order to provide the brains or logic to power the UI element. You'll be creating actions and outlets that will be used to change the visuals or respond to input.

Create a new code file using File > New > File > iOS > Source > Cocoa Touch Class:

On the next screen, subclass the UIView class and choose Swift:

6. Connect the XIB file and the Swift code file together using the “File’s Owner” attribute

The XIB user interface file will be loaded when the view is ready to display on your iPhone. In order for it to work with your code file, you'll need to make it aware of what Swift file is going to preform all the logic.

On the right panel, set the Custom Class name to the same name as your Swift code and XIB file:

7. Add code to load from a .xib file using the NSBundle and UINib classes

I wish the tutorial could end here. Unfortunately, Apple doesn't provide an easy way to connect the UI you designed to your UIView subclass. To connect the two, you are going to write some code that will load the user interface and place it inside the edges of the view in code.

Create two methods to load the .xib file from the bundle in which it’s located:

    // Our custom view from the XIB file
    var view: UIView!

    func xibSetup() {
        view = loadViewFromNib()

        // use bounds not frame or it'll be offset
        view.frame = bounds

        // Make the view stretch with containing view
        view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
        // Adding custom subview on top of our view (over any custom drawing > see note below)
        addSubview(view)
    }

    func loadViewFromNib() -> UIView {

        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: nibName, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

        return view
    }

You need to add a property to store the view that will be loaded from the XIB file. It might seem a little odd to store a view as a property within a UIView object. A custom UIView is just a composition or hierarchy of UIView objects that use position, size, color, and images to create the final look.

You'll also add two methods that will assist with the following step.

The first method, xibSetup() will be called from the initializers in the next step, when the UI is loaded. The loadViewFromNib() method will do the complex loading work from the file system.

8. Implement the initializers and leverage the code to load from an .xib file

Leverage the common code in both initializers used in UIView creation using your xibSetup() method.

You must make sure to call the super methods so that the correct UIView initialization occurs. The init(coder:) is called when creating your view within another interface file. The init(frame:) is called when creating your view programmatically (see below).

    override init(frame: CGRect) {
        // 1. setup any properties here

        // 2. call super.init(frame:)
        super.init(frame: frame)

        // 3. Setup view from .xib file
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        // 1. setup any properties here

        // 2. call super.init(coder:)
        super.init(coder: aDecoder)

        // 3. Setup view from .xib file
        xibSetup()
    } 

9. Add a new UIView object to your Storyboard file and set the Custom Class name

Drag a UIView object onto the Storyboard canvas for your current View Controller. Resize the UIView element to fit a smaller portion of the app screen. Note: If it’s the first object you add, it’ll be huge.

Temporarily, change the UIView color to non-white, so you can see it:

To get the Storyboard to load the correct UI element, you'll need to set this "blank" UIView to act as the placeholder for your custom class. Doing so will trigger the init(coder:) method to create your custom UIView at run-time. You can learn more about this process by reading the top description from Apple's UIView Reference or the UIViewController Reference (click "more..." to read the hidden description).

You will need to change the class name to “CustomView” in the Identity Inspector:

10. Make the CustomView.swift file IBDesignable

At the top of your Swift code file, you need to add @IBDesignable in Swift. Xcode will now compile (takes a few seconds) and embed your custom UIView inside a Storyboard file.

    import UIKit

    @IBDesignable class CustomView: UIView {

    }

11. Click on/off Main.storyboard to get Xcode to refresh your custom UIView

You should be able to make changes to your CustomView and see the changes in the Storyboard. Sometimes this can be buggy, especially if there is an issue. To force an update you can click on/off the Storyboard file, or you can use the new menu bar option described below.

An easier option is to select the Storyboard file and click on a custom view that's breaking. In the menu bar, do the following:  

Editor > Debug Selected Views

OR

Editor > Refresh All Views

Tip: Watch a video on how this works in Xcode 6.1.1

12. Add IBOutlets and IBActions to connect code and UI together

To work with any of the UI in code you must make connections with Outlets and Actions.

Open the Assistant Editor and then you can right-click and drag from the user interface into the associated code file. Create outlets for any UITextField, UIButton, or UILabel if you need to change the text. If you have a button, you'll need to setup an action to invoke a method using  the style buttonNamePressed(sender:).

You should have a set of outlets and actions like the following:

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var button: UIButton!
    @IBAction func buttonPressed(sender: AnyObject) {
        // do something
    }

Note: If you display an image, the UIImageView will need clipToBounds set to true to prevent artifacts and overdrawing.

13. Make an IBInspectable attribute to change the image at design time

The IBInspectable property allows you to expose functionality in code that can alter the appearance of the custom UIView. You can expose properties that are either computed, which gives you a hook to update the appearance.

Add code to alter the image using a computed property around the imageView outlet:

    @IBInspectable var image: UIImage? {
        get {
            return imageView.image
        }
        set(image) {
            imageView.image = image
        }
    }

14. Change the image at design time using the Attribute Inspector property

You can alter the appearance or settings for your custom UIView by changing parameters in the Attribute Inspector. Any property that you tag with @IBInspectable will be visible at the very top of the list of settings.

Change the text or even a corner radius using a computed property and the view.layer.cornerRadius value.

Resources

  1. Custom UIView with IBDesignable Elements - Apple’s Objective-C Code Sample
  2. Building Custom UI Elements With IBDesignable and IBInspectable - Think and Build
  3. How to Build a Custom (and “designable”) Control in Swift - Think and Build
  4. Debugging Xcode Live Rendering - Morten Bogh
  5. Nib Designable - Xcode Live Rendering With Nibs Made Easy - Morten Bogh

Summary

You can use this technique to create your own UIControl subclasses, just like UIButton or UILabel. UIControl provides the Target/Action model of interaction that allows you to create buttons, sliders, and more.

Using @IBInspectable you can also provide computed properties that can be changed using the Attributes Inspector at design time (convenient for non-programmers).

Connect

  1. Share this post on Twitter

  2. Let me know how you use it at @PaulSolt

  3. Checkout my Swift iPhone app courses

  4. Download the source code on github

Swift - Cannot use += operator with String! from UITextField or UITextArea - Implicitly Unwrapped Optional

I'm guessing this is a bug (submitted bug #19223563, but if it isn't, you'll be surprised to learn that in Xcode Version 6.1.1 you cannot append to a String from a UITextView or UITextField.

The problem is that both the UITextField and the UITextView are using an implicitly unwrapped optional String (String!) as the type. This is messing up the +=, so you either have to break apart the statement (defeating the purpose of +=). Or you have do a force unwrap before the +=.

If you don't you'll get a strange Swift Compiler Error stating that 'String!' is not identical to 'UInt8'

var numberString: String! = "0"
numberString = numberString + "2"

numberString! += "1"
numberString += "1" //'String!' is not identical to 'UInt8'

var textView = UITextView()
textView.text += "More text" //'String!' is not identical to 'UInt8'

var textField = UITextField()
textField.text += "Even more text" //'String!' is not identical to 'UInt8'

WatchKit Swift Tutorial - Download Xcode 6.2 Beta and Start Making Apple Watch Apps

The first beta of the WatchKit SDK was launched this past week and developers can now start making apps.

There are some limitations, but Apple released a lot more than was expected for WatchKit. There's a lot you'll be able to do within the restrictions, which are related to the embedded hardware (i.e.: Apple Watch) and it's battery life.

For now, until Native Apple Watch support is added sometime next year (fingers crossed), you'll have to work within the provided mechanisms. If something doesn't work, you'll need to let Apple know by submitting bug reports, feature requests, or enhancements to WatchKit or Xcode.

Known Limitations of WatchKit in Xcode 6.2 Beta

  1. Updated: (Beta 2 allows Watch apps to trigger iPhone apps) You cannot press a button on Apple Watch to cause an action in your iPhone app.
  2. You cannot create or use gestures. All gestures are controlled by Apple.
  3. You cannot move objects around screen by CGPoint position (Maybe with tableview hide/show).
  4. You cannot add UI elements at run-time, everything is design time before app runs.
  5. You cannot make fluid 2D/3D games like Flappy birds (Maybe jittery gameplay with image animations + hiding/showing hackery, + borderless button).
  6. Animations are image-based (in Apple's sample they include 360 images to show a full circle progress bar = 2mb image data).
  7. Your code runs on your iPhone app in an extension (new in iOS 8), and your UI + images reside on the Apple Watch.
  8. Your app will stop if the Apple Watch and iPhone are separated beyond the range of bluetooth. Only Apple can run native apps on device.
  9. You can only use storyboard files for UI (no .xib files).
  10. You must create an iPhone app Xcode project and add Apple Watch targets to create an app.
  11. You cannot use a physical iPhone with the Apple Watch simulator. Only available in simulation on Mac.

With some of the limitations in perspective, you can design apps that provide meaningful information that compliment your iPhone app. The watch is intended to show information that can be used quickly, and it isn't designed as a gaming device because of it's limited battery and processing power.

Download and install Xcode 6.2 Beta from the Apple Developer Website

Go to https://developer.apple.com/watchkit/ and you can download the beta versions of Xcode 6.2 and iOS 8.2 for testing out features relating to Apple Watch.

Scroll down to the bottom and read both the Apple Watch Design Guide and the Apple Watch Programming Guide.

Download Xcode 6.2 Beta using this link.

Getting Started with WatchKit in Xcode 6.2 Beta

In order to bring you up to speed, I've created a short YouTube video series in 4K resolution that demonstrates how to create a WatchKit app using timers. There are some bugs, but this is the best place to start if you want to work with some cutting edge technology.

Please submit any bug reports to http://bugreport.apple.com and you can help Apple improve WatchKit. Duplicate bug requests are a good thing, because it helps Apple see the more common issues.

Download the Xcode project with the Apple Watch app from github.

Swift WatchKit Tutorial 1 - Get Started with WatchKit to Make apps for the Apple Watch

WatchKit Swift Tutorial 2 - Getting Started with your First WatchKit App in Xcode 6 2 Beta

WatchKit Swift Tutorial 3 - Apple Watch User Interface Explained

WatchKit Swift Tutorial 4 - Connect the Apple Watch User Interface to Code

WatchKit Swift Tutorial 5 - Setup a NSTimer in Swift with Apple Watch

WatchKit Swift Tutorial 6 - Synchronize NSTimer and WKInterfaceTimer

Connect