Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Swift Nixie Tube Display Component

DZone's Guide to

A Swift Nixie Tube Display Component

This tutorial for a simple Swift component displays numbers and strings in a skeuomorphic, retro style.

· Mobile Zone
Free Resource

Launching an app doesn’t need to be daunting. Whether you’re just getting started or need a refresher on mobile app testing best practices, this guide is your resource! Brought to you in partnership with Perfecto

I’m a chap of a certain age who can still remember Nixie Tubes in common use: when I was growing up we had a digital clock that displayed the time with them and, back then, it was the height of technical sophistication. When I stumbled across these assets, I couldn’t resist creating a simple Swift component to display numbers and simple strings in an extremely skeuomorphic, retro style.

My FMNixieDigitDisplay component is super easy to implement: after constructing an instance and adding it to a target view:

    let nixieDigitDisplay = FMNixieDigitDisplay(numberOfDigits: 8)
    view.addSubview(nixieDigitDisplay)

Its intrinsicContentSize can be used for sizing and positing. Here, I centre it on its parent view:

    nixieDigitDisplay.frame = CGRect(
        x: view.frame.width / 2 - nixieDigitDisplay.intrinsicContentSize().width / 2,
        y: view.frame.height / 2 - nixieDigitDisplay.intrinsicContentSize().height / 2,
        width: nixieDigitDisplay.intrinsicContentSize().width,
        height: nixieDigitDisplay.intrinsicContentSize().height)

The component has three setValue functions which support integers, floats or simple strings:

    nixieDigitDisplay.setValue(int: 1234)
    nixieDigitDisplay.setValue(float: 123.456)
    nixieDigitDisplay.setValue(string: "12.34-56")

The only non-numeric characters the display supports are dashes and dots. Any other characters will be displayed as an empty space.

The FMNixieDigitDisplay component contains an array of FMNixieDigit which display the individual digits. To get the smooth cross fade as the digit changes, I use UIView.transitionFromView to transition between two UIImageView instances.

For example, when the FMNixieDigit is first created, its useEven Boolean property is set to true and of its two image views evenDigitViewand oddDigitView, only oddDigitView has been added to the view. When its value changes, it sets the image property of evenDigitViewand executes:

    UIView.transitionFromView(oddDigitView, toView: evenDigitView, duration: 0.5, options: animationOptions, completion: nil)

This code removes oddDigitView from its superview and adds evenDigitView to that same superview with a dissolve. By togglinguseEven with each change, I get a smooth transition with each time.

When I first started writing FMNixieDigitDisplay, my only thought was displaying integers, so its setValue(Int) uses the base 10 logarithm of the value to figure out how many characters are required and then iterates that many times, progressively raising it to increasing powers of ten to find the current digit with a modulo 10:

    let numberOfChars = Int(ceil(log10(Double(value))))

    for i in 0 ..< numberOfChars
    {
        let digitValue = floor((Double(value) / pow(10.0, Double(i)))) % 10

        nixieDigits[i].value = Int(digitValue)
    }

I then thought it would be nice to support floating point numbers, so added more code break up the value into its integer part of fractional part using modf() and striding backwards through the fractional part:

    for i in (numberOfDigits - numberOfChars - 1).stride(to: 0, by: -1)
    {
        let digitValue = modf(value).1 * pow(10.0, Float(numberOfDigits - numberOfChars - i)) % 10

        if i - 1 >= 0
        {
            nixieDigits[i - 1].value = Int(digitValue)
        }
    }

Finally, I decided to add a setValue(String) to display simple strings consisting of numbers, dots and dashes. This iterates of the value’scharacters and sets each digit accordingly. With hindsight, both the integer and float implementations of setValue() could have used this technique and kept the code a whole lot simpler. However, I’m quite fond of those first two functions and since this isn’t production code, I’ve kept all the code in.

The demonstration view controller code uses an NSTimer to update an instance of FMNixieDigitDisplay every second with the current time. Here, I break up an NSDate into its component parts and form a string to display the time:

    let date = NSDate()
    let calendar = NSCalendar.currentCalendar()
    let components = calendar.components(
        NSCalendarUnit(rawValue: NSCalendarUnit.Hour.rawValue | NSCalendarUnit.Minute.rawValue | NSCalendarUnit.Second.rawValue),
        fromDate: date)
    let hour = components.hour
    let minute = components.minute
    let second = components.second

    print(hour, minute, second)

    nixieDigitDisplay.setValue(string: "\(hour).\(minute)-\(second)")

All the code for this component is available at my GitHub repository here. If you plan to use this commercially, please pay attention to the usage notes for the assets here. Enjoy!

Keep up with the latest DevTest Jargon with the latest Mobile DevTest Dictionary. Brought to you in partnership with Perfecto.

Topics:
mobile ,swift ,ios

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}