Swift: CGRect, CGSize и CGPoint

Вы (вероятно) делаете это неправильно

Прошло около восьми месяцев с тех пор, как я впервые решил полностью прыгнуть в червоточину Свифта. За эти месяцы я постепенно научился прекращать писать шаблоны кода в стиле Objective-C с синтаксисом Swift и начал фактически использовать преимущества нового языка.

Но недавно я заметил одну вещь: я до сих пор использую уродливый синтаксис, отличный от Swift, со всеми своими CGGeometry структурами.

CGRect, CGSize, CGPoint

Синтаксис C. Овца в волчьей шкуре

У меня есть большое предчувствие, что многие разработчики Swift будут виноваты в этом. Поднимите руку, если вы использовали что-либо из этого в своем коде:

let rect = CGRectMake(0, 0, 100, 100)
let point = CGPointMake(0, 0)
let size = CGSizeMake(100, 100)

Обновление: в Swift 3 эти функции были удалены.

Да, я так и думал. Но не волнуйтесь, стыдиться нечего… пока.

Проблема в том, что это нарушает соглашение о кодировании Swift, оно будет по-прежнему работать, но оно больше похоже на то, что, если оно принадлежит Objective-C или даже, осмелюсь сказать, Джава. * вздрагивает *

Опытный разработчик iOS или OSX с первого взгляда может легко сказать вам, что делает этот код. Им не нужны метки аргументов в CGGeometry структурах, потому что каждый знает эти аргументы и их последовательность наизусть. Но они этого не делают.

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

let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let size = CGSize(width: 100, height: 100)
let point = CGPoint(x: 0, y: 0)

Из-за небольшого дополнительного синтаксиса многословность нашего кода полностью понятна с первого взгляда. Кроме того, дополнительным преимуществом использования инициализатора структуры CGGeometry является то, что теперь мы не ограничены только использованием CGFloat в качестве аргументов, мы также можем передавать Int и Double!

Нуль

let rect = CGRectZero
let size = CGSizeZero
let point = CGPointZero

Обновление: в Swift 3 эти функции были удалены.

Пока мы занимаемся этим, вы, вероятно, все еще используете их, амирит? lel.

Мы также должны обновить их, чтобы использовать новый синтаксис Swiftier. Но не волнуйтесь, синтаксис всего лишь на один лишний символ длиннее. Сможете угадать, что это?

let rect = CGRect.zero
let size = CGSize.zero
let point = CGPoint.zero

Еще одна вещь, которая поможет вам понять это, заключается в том, что подсветка синтаксиса Xcode будет, в зависимости от вашей цветовой схемы, выделять .zero, что немного облегчает вашу когнитивную нагрузку.

Получение значения

CGRect frame = CGRectMake(0, 0, 100, 100)
CGFloat width = CGRectGetWidth(frame)
CGFloat height = CGRectGetHeight(frame)
CGFloat maxX = CGRectGetMaxX(frame)
CGFloat maxY = CGRectGetMaxY(frame)

Обновление: в Swift 3 эти функции были удалены.

Если бы вы были или остаетесь хорошим гражданином Objective-C, вы бы использовали эти геттеры значений, когда вам нужно определенное значение прямоугольника. Но подождите секунду, почему бы нам просто не получить доступ к значениям напрямую?

CGFloat width = frame.size.width
CGFloat height = frame.size.height

По этой причине ваши приложения должны избегать прямого чтения и записи данных, хранящихся в структуре данных CGRect. Вместо этого используйте описанные здесь функции для управления прямоугольниками и получения их характеристик.

- Справочная документация Apple, CGGeometry

Наверное, многие из вас еще не позаботились о таких формальностях, но это нормально. Swift избавил нас от такого некрасивого API и вместо этого предоставил простые переменные с точечной нотацией.

let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let width = frame.width
let height = frame.height
let maxX = frame.maxX
let maxY = frame.maxY

На игровых площадках Swift есть еще множество примеров этих тонкостей, которые я добавил в дополнение к этому посту. Ссылка будет внизу страницы.

Изменчивость

let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = UIView(frame: frame)
view.frame.origin.x += 10

Вы можете не только изменять отдельные значения рамки представления, но также можете полностью заменять целые подструктуры. Вот:

let view = UIView(frame: .zero)
view.frame.size = CGSize(width: 10, height: 10)
view.frame.origin = CGPoint(x: 10, y: 10)

Одной этой маленькой функции уже достаточно, чтобы отказаться от использования Objective-C. Изменяемость на CGRects без необходимости полностью создавать новый и сбрасывать значение. Менее двух лет назад мы, разработчики Objective-C, были вынуждены написать такой код модификации кадра:

CGRect frame = CGRectMake(0, 0, 100, 100);
UIView *view = [[UIView alloc] initWithFrame: frame];
CGRect newFrame = view.frame;
newFrame.size.width = view.frame.origin.x + 10;
view.frame = newFrame;

Не знаю, как вы, но это написание меня очень напрягло. Создание структуры newFrame из фрейма view s, ее изменение и повторная установка свойства frame на view. Нет, спасибо, никогда, никогда, пожалуйста.

Не забывай

В UIKit также есть пара других конструкций, к которым применимы эти изменения:

UIEdgeInsets 
var edgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
edgeInsets.top += 10
UIOffset
var offset = UIOffset(horizontal: 10, vertical: 10)
offset.vertical += 10

Образец кода этого сообщения можно найти на GitHub.

Если вам нравится то, что вы прочитали сегодня, вы можете проверить наши другие статьи или хотите связаться, отправьте мне твит или подпишитесь на меня в Twitter, это действительно делает мой день. Я также организовываю Playgrounds Conference в Мельбурне, Австралия, и хочу увидеть вас на следующем мероприятии.