Статьи

Компонент для отображения трубки Swift Nixie

Я парень определенного возраста, который до сих пор помнит   общепринятую версию Nixie Tubes : когда я рос, у нас были цифровые часы, которые отображали время вместе с ними, и тогда это был верх технического совершенства. Когда я наткнулся на  эти активы , я не удержался от создания  простого компонента Swift  для отображения чисел и простых строк в чрезвычайно скейоморфном стиле ретро.

Мой   компонент FMNixieDigitDisplay очень прост в реализации: после создания экземпляра и добавления его в целевое представление:

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

Его intrinsicContentSize может использоваться для определения размера и размещения. Здесь я центрирую его на родительском представлении:

    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)

Компонент имеет три функции setValue, которые поддерживают целые числа, числа с плавающей запятой или простые строки:

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

Единственные нечисловые символы, которые поддерживает дисплей, — это тире и точки. Любые другие символы будут отображаться как пустое место.

Компонент FMNixieDigitDisplay содержит массив  FMNixieDigit,  который отображает отдельные цифры. Чтобы получить плавное перекрестное затухание при изменении цифры, я использую UIView.transitionFromView для перехода между двумя экземплярами UIImageView.

Например, когда FMNixieDigit создается впервые, его логическое свойство useEven устанавливается в значение true, а из двух его представлений изображения даже DigitView и oddDigitView в представление добавляется только oddDigitView. Когда его значение изменяется, он устанавливает свойство изображения evenDigitView и выполняет:

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

Этот код удаляет oddDigitView из своего суперпредставления и добавляет в этот же суперпредставление evenDigitView с распадом. С помощью togglinguseEven при каждом изменении я получаю плавный переход с каждым разом.

Когда я впервые начал писать FMNixieDigitDisplay, моей единственной мыслью было отображение целых чисел, поэтому его setValue (Int) использует логарифм значения 10, чтобы выяснить, сколько символов требуется, а затем повторяет это много раз, постепенно увеличивая его до возрастающей степени десять, чтобы найти текущую цифру по модулю 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)
    }

Затем я подумал, что было бы неплохо поддерживать числа с плавающей запятой, поэтому добавил больше кода, разбивающего значение на целую часть дробной части, используя modf () и продвигаясь назад через дробную часть:

    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)
        }
    }

Наконец, я решил добавить setValue (String) для отображения простых строк, состоящих из цифр, точек и тире. Это повторяет символы значения и устанавливает каждую цифру соответственно. Оглядываясь назад, и целочисленные, и реализации с плавающей точкой setValue () могли бы использовать эту технику и сделать код намного проще. Тем не менее, мне очень нравятся эти первые две функции, и, поскольку это не рабочий код, я сохранил весь код.

Код контроллера демонстрационного представления использует NSTimer для обновления экземпляра FMNixieDigitDisplay каждую секунду с текущим временем. Здесь я разбиваю NSDate на составные части и формирую строку для отображения времени:

    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)")

Весь код для этого компонента доступен в  моем репозитории GitHub здесь . Если вы планируете использовать это в коммерческих целях, пожалуйста, обратите внимание на  примечания по использованию для активов здесь . Наслаждайтесь!