Swift и 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
от Float от get-go. Но когда многие переменные, взятые из-за места, участвуют в вычислении предлагаемых элементов CGRect, там много кастинга.
Подробный инициализатор
Еще одно раздражение - это то, что происходит, когда придет время создать новый CGRect. Несмотря на документацию, нет инициализатора со значениями, но без меток. Это не скомпилируется, потому что у нас есть Doubles:
let d = 2.0
var r3 = CGRect(d, d, d, d)
Но даже если мы набрасываем d
на Float, мы не компилируем:
Отсутствуют метки аргументов 'x: y: width: height:' в вызове
Итак, мы заканчиваем падение на CGRectMake
, что не улучшается на Objective-C. А иногда CGRectMake и CGSizeMake не улучшаются. Рассмотрим этот фактический код из одного из моих приложений:
let kSEP : Float = 2.0
let intercellSpacing = CGSizeMake(kSEP, kSEP);
В одном из моих проектов это работает. В другом случае это таинственно терпит неудачу - тот же самый код! - с этой ошибкой:
"NSNumber" не является подтипом "CGFloat"
Как будто, иногда, Swift пытается "пересечь мост", бросая Float в NSNumber, что, конечно же, неправильно, когда что-то на другой стороне моста ожидает CGFloat. Я еще не понял, какая разница между двумя проектами, которые приводят к появлению ошибки в одном, но не в другом (возможно, у кого-то еще есть).
ПРИМЕЧАНИЕ.. Возможно, я выяснил эту проблему: она зависит от настройки Build Build Architecture Only, которая, в свою очередь, предполагает, что это 64-разрядная проблема. Это имеет смысл, поскольку Float не будет соответствовать CGFloat на 64-битном устройстве. Это означает, что проблема несоответствия импеданса еще хуже, чем я думал.
Заключение
Я ищу практические слова мудрости по этой теме. Я думаю, кто-то, возможно, разработал некоторые расширения CGRect и CGPoint, которые значительно облегчат жизнь. (Или, возможно, кто-то написал лодку дополнительных арифметических перегрузок функций оператора, так что объединение CGFloat с Int или Double "просто работает" - если это возможно.)
Ответы
Ответ 1
Я написал библиотеку, которая обрабатывает перегрузку оператора, чтобы разрешить взаимодействие между Int, CGFloat и Double.
https://github.com/seivan/ScalarArithmetic
Начиная с Beta 5, вот список вещей, которые вы в настоящее время не можете сделать с ванилью Swift.
https://github.com/seivan/ScalarArithmetic#sample
Я предлагаю запустить тестовый набор с ScalarArithmetic и без него, чтобы увидеть, что происходит.
Ответ 2
Явно набрав scale
to CGFloat
, как вы обнаружили, действительно способ справиться с проблемой ввода в swift. Для справки для других:
let scale: CGFloat = 2.0
let r = self.view.bounds
var r2 = CGRect()
r2.size.width = r.width * scale
Не уверен, как ответить на второй вопрос, вы можете отправить его отдельно с другим заголовком.
Update:
Создатель Swift и ведущий разработчик Крис Лэттнер сказал об этом на форуме Apple Developer Forum 4 июля 2014 года:
Что здесь происходит, так это то, что CGFloat является титалиасом для Float или Двойной в зависимости от того, строите ли вы 32 или 64 бит. Это как работает Objective-C, но проблематично в Swift потому что Swift не допускает неявных преобразований.
Мы знаем о эта проблема и считаем ее серьезной: мы оцениваем несколько различные решения прямо сейчас, и выйдет в бета-версии. Как вы заметили, вы можете справиться с этим сегодня, перейдя на Double. Это неэлегантно, но эффективно: -)
Обновление в Xcode 6 Beta 5:
CGFloat можно построить из любого типа Integer (включая ) и наоборот. (17670817)
Ответ 3
Я создал расширение для 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 Initializer, вы получаете ошибку меток отсутствующих аргументов, потому что вы оставили метки, вам нужно CGRect(x: d, y: d, width: d, height: d)
, вы не можете оставить ярлыки, если только не существует только одного аргумента.