Подтвердить что ты не робот

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 "просто работает" - если это возможно.)

4b9b3361

Ответ 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), вы не можете оставить ярлыки, если только не существует только одного аргумента.