Swift numerics и CGFloat (CGPoint, CGRect и др.)


Я нахожу быстрые цифры особенно неуклюжими, когда, как это часто бывает в реальной жизни, мне приходится общаться с Cocoa Touch в отношении CGRect и CGPoint (например, потому что мы говорим о чем-то frame или bounds).

CGFloat против Double

рассмотрим следующий невинный код из подкласса UIViewController:

let scale = 2.0
let r = self.view.bounds
var r2 = CGRect()
r2.size.width = r.size.width * scale

этот код не компилируется, с обычной загадочной ошибкой на последнем линия:

не удалось найти перегрузки для '*', который принимает предоставленные аргументы

эта ошибка, как я уверен, вы уже знаете, указывает на какое-то несоответствие импеданса между типами. r.size.width поступает как CGFloat, который будет автоматически обмениваться с Swift Float, но не может взаимодействовать с переменной Swift Double (которая по умолчанию является чем scale есть).

пример искусственно короткий, так что есть искусственно простое решение, которое состоит в том, чтобы бросить scale к поплавку с самого начала. Но когда многие переменные, взятые со всего места, участвуют в вычислении элементов предлагаемого CGRect, нужно сделать много кастинга.

Подробный Инициализатора

еще одно раздражение-это то, что происходит, когда приходит время создавать новый CGRect. Несмотря на документацию, нет инициализатора со значениями, но без этикетки. Это не скомпилируется, потому что у нас есть Удваивается:

let d = 2.0
var r3 = CGRect(d, d, d, d)

но даже если мы бросим d к поплавку, мы не компилируем:

отсутствуют метки аргументов ' x:y:width: height:' in call

так что мы в конечном итоге падаем обратно на CGRectMake, что не является улучшением на Objective-C. И иногда CGRectMake и CGSizeMake не являются улучшением. Рассмотрим этот код из одного из моих приложений:

let kSEP : Float = 2.0
let intercellSpacing = CGSizeMake(kSEP, kSEP);

в одном из моих проектов, который работает. В другом, он таинственно терпит неудачу - точно такой же код! - с этой ошибкой:

'NSNumber' не является подтипом 'CGFloat'

иногда Свифт пытается "пересечь мост", бросая поплавок в NSNumber, что, конечно, неправильно, когда то, что находится на другой стороне моста, ожидает CGFloat. Я еще не понял, в чем разница между двумя проектами, что приводит к появлению ошибки в одном, но не в другом (возможно, кто-то другой имеет.)

Примечание: возможно, я понял эту проблему: похоже, это зависит от настройки сборки активной архитектуры, которая, в свою очередь, предполагает, что это 64-разрядная проблема. Что имеет смысл, так как Float не будет соответствовать CGFloat на 64-разрядном устройстве. Это означает, что проблема несоответствия импеданса еще хуже, чем я думал.

вывод

Я ищу практические слова мудрости по этой теме. Я думаю, что кто-то возможно, он разработал некоторые расширения CGRect и CGPoint, которые сделают жизнь намного проще. (Или, возможно, кто - то написал лодку дополнительных перегрузок арифметических функций оператора, таких что объединение CGFloat с Int или Double "просто работает" - если это возможно.)

3 59

3 ответа:

Я написал библиотеку, которая обрабатывает перегрузку оператора, чтобы обеспечить взаимодействие между Int, CGFloat и Double.

https://github.com/seivan/ScalarArithmetic

начиная с бета-версии 5, Вот список вещей, которые вы в настоящее время не можете сделать с vanilla Swift. https://github.com/seivan/ScalarArithmetic#sample

Я предлагаю запустить тестовый набор со Скаляритом и без него, чтобы посмотреть, что происходит.

явно введя scale to CGFloat, Как вы обнаружили, это действительно способ справиться с проблемой ввода в swift. Для справки для других:

let scale: CGFloat = 2.0
let r = self.view.bounds
var r2 = CGRect()
r2.size.width = r.width * scale

не знаю, как ответить на ваш второй вопрос, вы можете разместить его отдельно с другим названием.

обновление:

Swift создатель и ведущий разработчик Крис Латтнер имел это сказать по этому вопросу на Форум Разработчиков Apple 4 июля, 2014:

что здесь происходит, так это то, что CGFloat является типом для любого поплавка или двухместная в зависимости от того, строите ли вы для 32 или 64-бит. Именно так работает Objective-C, но в Swift это проблематично потому что Swift не допускает неявных преобразований.

мы в курсе эту проблему и считают серьезной: мы оцениваем несколько различные решения прямо сейчас и полетят в более поздней бета-версии. Как вы заметили, вы справится с этим можно уже сегодня, бросив в дубль. Это неэлегантно, но эффективно : -)

Обновление В Xcode 6 Beta 5:

CGFloat может быть построен из любого целочисленного типа (включая целочисленные типы размера) и наоборот. (17670817)

Я создал расширение для Double и Int, которое добавляет к ним вычисленное свойство CGFloatValue.

extension Double {
    var CGFloatValue: CGFloat {
        get {
            return CGFloat(self)
        }
    }
}
extension Int {
    var CGFloatValue: CGFloat {
        get {
            return CGFloat(self)
        }
    }
}

вы бы получить доступ к нему с помощью let someCGFloat = someDoubleOrInt.CGFloatValue

кроме того, что касается вашего инициализатора CGRect, вы получаете ошибку отсутствующих меток аргументов, потому что вы оставили метки, вам нужно CGRect(x: d, y: d, width: d, height: d) вы не можете оставить метки, если есть только один аргумент.