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 ответа:
Я написал библиотеку, которая обрабатывает перегрузку оператора, чтобы обеспечить взаимодействие между Int, CGFloat и Double.
https://github.com/seivan/ScalarArithmetic
начиная с бета-версии 5, Вот список вещей, которые вы в настоящее время не можете сделать с vanilla Swift. https://github.com/seivan/ScalarArithmetic#sample
Я предлагаю запустить тестовый набор со Скаляритом и без него, чтобы посмотреть, что происходит.
явно введя
scale
toCGFloat
, Как вы обнаружили, это действительно способ справиться с проблемой ввода в 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)
вы не можете оставить метки, если есть только один аргумент.