← Назад к вопросам

Всегда ли frame и bounds одинаковы?

1.0 Junior🔥 231 комментариев
#UIKit и верстка

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Всегда ли frame и bounds одинаковы?

Нет, frame и bounds UIView далеко не всегда одинаковы. Это одно из фундаментальных различий в системе координат iOS и UIKit, и понимание этой разницы критически важно для корректного позиционирования, трансформаций и отрисовки view.

Ключевое различие в системе координат

Основная разница заключается в точке отсчета (origin) и назначении этих свойств:

  • frame (CGRect): Описывает положение и размер view в системе координат ее супервью (родителя). Его origin (например, (x: 20, y: 50)) отвечает на вопрос: "Где левый верхний угол этой view расположен относительно левого верхнего угла ее родителя?".
  • bounds (CGRect): Описывает положение и размер view в ее собственной внутренней системе координат. По умолчанию его origin равен (0, 0). Он отвечает на вопрос: "Какая видимая часть внутреннего содержимого этой view (например, ее subviews или рисунок)?" origin bounds можно менять, чтобы "прокручивать" содержимое.

Когда frame и bounds РАЗНЫ? (Типичные случаи)

  1. Вращение и трансформации (Affine Transforms)
    Это самый важный и показательный случай. Свойство `frame` становится неопределенным (`undefined`), если к view применена не-идентичная трансформация вращения или масштабирования. Его вычисление является затратной операцией. `bounds` и `center` при этом остаются валидными и используются для корректной работы с трансформированными view.

```swift
let view = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
view.transform = CGAffineTransform(rotationAngle: .pi / 6) // Поворот на 30°

// После трансформации:
print(view.frame)    // Может показать, например, (35.0, 35.0, 141.0, 141.0)
print(view.bounds)   // Останется (0.0, 0.0, 100.0, 100.0)
// frame изменился, чтобы описать bounding box повернутой view.
// bounds не изменился, так как внутренняя геометрия view та же.
```

2. Изменение origin свойства bounds

    Это активно используется в `UIScrollView` и ее подклассах (`UITableView`, `UICollectionView`). Сдвигая `bounds.origin`, мы сообщаем view, какая часть ее внутреннего контента должна быть отрисована в левом верхнем углу ее видимой области.

```swift
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 320, height: 568))
scrollView.contentSize = CGSize(width: 1000, height: 568)

// При прокрутке вправо на 150 точек:
scrollView.bounds.origin = CGPoint(x: 150, y: 0)

print(scrollView.frame)  // Останется (0.0, 0.0, 320.0, 568.0) - положение на экране не изменилось
print(scrollView.bounds) // Станет (150.0, 0.0, 320.0, 568.0) - видимая область сместилась
```

3. Вложенность view (без трансформаций)

    Даже в простом случае, если view не находится в самом левом верхнем углу родителя, `frame.origin` и `bounds.origin` будут разными.

```swift
let parentView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 800))
let childView = UIView(frame: CGRect(x: 100, y: 200, width: 150, height: 150))

parentView.addSubview(childView)

print(childView.frame)  // (100.0, 200.0, 150.0, 150.0) - относительно parentView
print(childView.bounds) // (0.0, 0.0, 150.0, 150.0)     - относительно себя
```

Когда frame и bounds МОГУТ БЫТЬ ОДИНАКОВЫ?

Единственный случай — когда view является корневой и не имеет трансформаций, и ее bounds.origin не менялся. Тогда:

  • frame.origin задается относительно супервью (которого нет, обычно это координаты окна).
  • bounds.origin по умолчанию (0, 0).

Но даже в этом случае их семантика разная: frame описывает положение в родительской системе координат, а bounds — внутреннюю геометрию. Их числовое равенство — ситуативное совпадение, а не правило.

Практическое правило для разработчика

  • Используйте frame, когда вам нужно:
    *   **Расположить или изменить размер view относительно ее супервью.**
    *   Получить **bounding box** view на экране (если нет трансформаций).
  • Используйте bounds, когда вам нужно:
    *   **Расположить что-либо внутри view** (например, добавить subview или нарисовать).
    *   Работать с **прокруткой** (`UIScrollView`).
    *   Корректно работать с view, к которой применены **трансформации** (вместе с `center`).
    *   Получить реальный внутренний размер view, не зависящий от трансформаций.

Таким образом, различие между frame и bounds — это не баг, а фича, обеспечивающая гибкость и мощь системы отрисовки UIKit. Их неодинаковость — это норма и ожидаемое поведение в большинстве нетривиальных случаев.