В чем разница между свойствами `frame` и `bounds` у UIView?

«В чем разница между свойствами `frame` и `bounds` у UIView?» — вопрос из категории UIKit, который задают на 41% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Свойство Система координат Описание Изменение origin
frame Система координат родительского view (superview). Определяет позицию и размер view относительно ее родителя. CGRect.origin — это точка (x, y) в координатах супервью. Перемещает view относительно родителя.
bounds Внутренняя (собственная) система координат view. (0, 0) — это левый верхний угол самой view. Определяет внутренний размер view и видимую область ее содержимого (как «окошко»). CGRect.origin здесь называется bounds.origin. Сдвигает систему координат содержимого внутри view (например, для скроллинга).

Ключевые отличия и правила:

  1. Зависимость: frame является производным свойством. Он вычисляется на основе center, bounds и transform. Прямое изменение frame может быть неточным, если задано вращение или скейлинг (transform).
  2. Трансформации: bounds не зависит от transform. Если вы вращаете view, ее frame (описывающий ограничивающий прямоугольник) изменится, а bounds.size — останется прежним.
  3. Использование bounds.origin: Это мощный инструмент. Изменение bounds.origin сдвигает внутреннюю систему координат, что приводит к смещению всех дочерних view (subviews) и отрисованного содержимого (например, drawRect:). Именно так работает скроллинг в UIScrollView.

Наглядный пример:

// Создаем view размером 100x100, позиционируем ее в (50, 50) у родителя.
let greenView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
greenView.backgroundColor = .green

print(greenView.frame)   // (50.0, 50.0, 100.0, 100.0) - относительно родителя
print(greenView.bounds)  // (0.0, 0.0, 100.0, 100.0) - свои внутренние координаты

// Меняем bounds.origin - сдвигаем «окошко», через которое view смотрит на свое содержимое.
greenView.bounds.origin = CGPoint(x: 20, y: 20)
// Теперь внутренняя система координат greenView сдвинута.
// Точка (0,0) в системе greenView теперь соответствует точке (20,20) в его собственной геометрии.
// Это приведет к смещению всех его subviews на 20pt влево и вверх.
print(greenView.frame)   // (50.0, 50.0, 100.0, 100.0) - НЕ ИЗМЕНИЛСЯ (позиция относительно родителя та же)
print(greenView.bounds)  // (20.0, 20.0, 100.0, 100.0) - origin изменился

Когда что использовать:

  • Используйте frame для позиционирования и изменения размера view внутри ее супервью.
  • Используйте bounds для работы с внутренним содержимым, его отрисовкой (drawRect:) и расположением дочерних view. Изменяйте bounds.origin для реализации скроллинга.