Какао: Какая разница между рамкой и границами?

587

UIViewи все его подклассы имеют свойства frameи bounds. Какая разница?

Mk12
источник
1
Понимание фрейма macoscope.com/blog/understanding-frame
onmyway133
для меня самое краткое объяснение здесь: videos.raywenderlich.com/courses/99-scroll-view-school/lessons/…
Влад
1
Там нет ничего краткого о видео 2:30. Я могу прочитать предложение или два гораздо быстрее, чем 2:30.
dsjoerg
Для меня гораздо яснее, если я так думаю: Frame = Bounds & Position
Serg

Ответы:

902

В пределах А. Н. UIView является прямоугольником , выраженным как местоположение (х, у) и размер (ширина, высота) по отношению к своей собственной системе координат (0,0).

Кадр из UIView представляет собой прямоугольник , выраженный как местоположение (х, у) и размер (ширина, высота) по отношению к надтаблице он содержится внутри.

Итак, представьте себе представление размером 100x100 (ширина x высота), расположенное на 25,25 (x, y) суперпредставления. Следующий код распечатывает границы и фрейм этого представления:

// This method is in the view controller of the superview
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"bounds.origin.x: %f", label.bounds.origin.x);
    NSLog(@"bounds.origin.y: %f", label.bounds.origin.y);
    NSLog(@"bounds.size.width: %f", label.bounds.size.width);
    NSLog(@"bounds.size.height: %f", label.bounds.size.height);

    NSLog(@"frame.origin.x: %f", label.frame.origin.x);
    NSLog(@"frame.origin.y: %f", label.frame.origin.y);
    NSLog(@"frame.size.width: %f", label.frame.size.width);
    NSLog(@"frame.size.height: %f", label.frame.size.height);
}

И вывод этого кода:

bounds.origin.x: 0
bounds.origin.y: 0
bounds.size.width: 100
bounds.size.height: 100

frame.origin.x: 25
frame.origin.y: 25
frame.size.width: 100
frame.size.height: 100

Итак, мы видим, что в обоих случаях ширина и высота вида одинаковы, независимо от того, смотрим ли мы на границы или рамку. Отличие состоит в расположении вида по оси x, y. В случае границ координаты x и y равны 0,0, поскольку эти координаты относятся к самому виду. Тем не менее, координаты фрейма x и y относятся к положению представления в родительском представлении (которое ранее мы говорили в 25, 25).

Существует также отличная презентация, которая охватывает UIViews. Смотрите слайды 1-20, которые не только объясняют разницу между рамками и границами, но также показывают наглядные примеры.

ши
источник
37
Поэтому не будет x, y для границ всегда равным 0,0, поскольку это местоположение объекта в ... самом. Или вы могли бы привести сценарий, где это не будет?
Mk12
5
Насколько я знаю, происхождение границ всегда будет 0,0
ши
77
На самом деле, bounds.origin может быть чем-то кроме 0,0. Используйте setBoundsOrigin: чтобы переместить / перевести источник. См. «Просмотр геометрии» в «Просмотр руководства по программированию для какао» для получения дополнительной информации.
Мелтеми
127
UIScrollView - это пример, в котором границы могут сдвигаться для отображения различных областей содержимого в представлении.
Брэд Ларсон
92
Небольшой совет - использование NSStringFromCGRect может сэкономить некоторое время для регистрации рецензий.
бериллий
647

Короткий ответ

frame = расположение и размер представления, используя систему координат родительского представления

  • Важно для: размещения представления в родительском

bounds = расположение и размер представления с использованием его собственной системы координат

  • Важно для: размещения содержимого представления или подпредставлений внутри себя

Подробный ответ

Чтобы помочь мне вспомнить рамку , я думаю о картинной рамке на стене . Рамка рисунка похожа на границу вида. Я могу повесить картину где угодно на стене. Таким же образом я могу поместить представление в любом месте внутри родительского представления (также называемого суперпредставлением). Родительский вид похож на стену. Начало системы координат в iOS находится слева вверху. Мы можем поместить наш вид в начало суперпредставления, установив координаты xy рамки просмотра в (0, 0), что похоже на вывешивание нашего изображения в самом верхнем левом углу стены. Чтобы переместить его вправо, увеличьте x, чтобы переместить его вниз, увеличьте y.

Чтобы помочь мне вспомнить границы , я вспоминаю баскетбольную площадку, где иногда баскетбол выбивают из-за пределов . Вы ведете мяч по всей баскетбольной площадке, но вам все равно, где находится сама площадка. Это может быть в тренажерном зале, или на улице в средней школе, или перед вашим домом. Это не важно Ты просто хочешь играть в баскетбол. Точно так же система координат границ вида заботится только о самом виде. Он ничего не знает о расположении представления в родительском представлении. Начало координат (точка (0, 0) по умолчанию) - это верхний левый угол представления. Любые подпредставления, которые имеет эта точка зрения, изложены в связи с этой точкой зрения. Это как перенести баскетбол в левый передний угол площадки.

Теперь путаница возникает, когда вы пытаетесь сравнить рамки и границы. Хотя на самом деле все не так плохо, как кажется на первый взгляд. Давайте использовать некоторые картинки, чтобы помочь нам понять.

Рамка против границ

На первом изображении слева мы видим вид, который находится в верхнем левом углу родительского вида. Желтый прямоугольник представляет рамку вида. Справа мы снова видим представление, но на этот раз родительское представление не отображается. Это потому, что границы не знают о родительском представлении. Зеленый прямоугольник представляет границы представления. Красная точка на обоих изображениях представляет собой начало кадра или рамки.

Frame
    origin = (0, 0)
    width = 80
    height = 130

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

введите описание изображения здесь

Таким образом, рамка и границы были точно такими же на этой картинке. Давайте посмотрим на пример, где они разные.

Frame
    origin = (40, 60)  // That is, x=40 and y=60
    width = 80
    height = 130

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

введите описание изображения здесь

Таким образом, вы можете видеть, что изменение координат xy кадра перемещает его в родительском представлении. Но содержание самого представления выглядит точно так же. Границы понятия не имеют, что все по-другому.

До сих пор ширина и высота рамки и границ были абсолютно одинаковыми. Хотя это не всегда так. Посмотрите, что произойдет, если мы повернем вид на 20 градусов по часовой стрелке. (Вращение осуществляется с помощью преобразования. См в документации и эти просматривать и слой примеры для получения дополнительной информации.)

Frame
    origin = (20, 52)  // These are just rough estimates.
    width = 118
    height = 187

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

введите описание изображения здесь

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

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

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

Важно отметить, что если вы преобразуете вид, то рамка становится неопределенной. На самом деле, желтая рамка, которую я нарисовал вокруг повернутых зеленых границ на изображении выше, на самом деле никогда не существует. Это означает, что если вы вращаете, масштабируете или делаете какое-то другое преобразование, вам больше не следует использовать значения кадра. Вы все еще можете использовать значения границ, хотя. Документы Apple предупреждают:

Важное замечание: Если transformсвойство представления не содержит преобразования идентификаторов, кадр этого представления не определен, как и результаты его автоматического изменения размеров.

Скорее неудачно с автоматическим изменением размера .... Но есть кое-что, что вы можете сделать.

Документы Apple утверждают:

При изменении transformсвойства вашего представления все преобразования выполняются относительно центральной точки представления.

Поэтому, если вам нужно переместить представление в родительском объекте после преобразования, вы можете сделать это, изменив view.centerкоординаты. Мол frame, centerиспользует систему координат родительского представления.

Хорошо, давайте избавимся от нашего вращения и сосредоточимся на границах. До сих пор происхождение границ всегда оставалось на (0, 0). Это не обязательно, однако. Что если в нашем представлении слишком большое подпредставление, которое слишком велико для одновременного отображения? Мы сделаем это UIImageViewс большим изображением. Вот снова наша вторая картинка сверху, но на этот раз мы можем видеть, как будет выглядеть весь контент подпредставления нашего представления.

Frame
    origin = (40, 60)
    width = 80
    height = 130

Bounds 
    origin = (0, 0)
    width = 80
    height = 130

введите описание изображения здесь

Только верхний левый угол изображения может уместиться в пределах границ вида. Теперь посмотрим, что произойдет, если мы изменим исходные координаты границ.

Frame
    origin = (40, 60)
    width = 80
    height = 130

Bounds 
    origin = (280, 70)
    width = 80
    height = 130

введите описание изображения здесь

В суперпредставлении рамка не сместилась, но содержимое внутри рамки изменилось, поскольку начало прямоугольника границ начинается в другой части представления. Это вся идея, стоящая за классом a UIScrollViewи его подклассами (например, a UITableView). Посмотрите Понимание UIScrollView для большего объяснения.

Когда использовать рамку, а когда использовать границы

Так как frameсвязывает местоположение вида в его родительском виде, вы используете его при внесении внешних изменений , таких как изменение его ширины или нахождение расстояния между видом и вершиной его родительского вида.

Используйте, boundsкогда вы делаете внутренние изменения , такие как рисование вещей или организация подпредставлений в представлении. Также используйте границы, чтобы получить размер вида, если вы произвели некоторую трансформацию на нем.

Статьи для дальнейшего исследования:

Apple документы

Связанные вопросы StackOverflow

Другие источники

Практикуй себя

В дополнение к чтению вышеупомянутых статей, мне очень помогает сделать тестовое приложение. Возможно, вы захотите попробовать сделать что-то подобное. (Я получил идею из этого видео курса, но, к сожалению, это не бесплатно.)

введите описание изображения здесь

Вот код для вашей справки:

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var myView: UIView!

    // Labels
    @IBOutlet weak var frameX: UILabel!
    @IBOutlet weak var frameY: UILabel!
    @IBOutlet weak var frameWidth: UILabel!
    @IBOutlet weak var frameHeight: UILabel!
    @IBOutlet weak var boundsX: UILabel!
    @IBOutlet weak var boundsY: UILabel!
    @IBOutlet weak var boundsWidth: UILabel!
    @IBOutlet weak var boundsHeight: UILabel!
    @IBOutlet weak var centerX: UILabel!
    @IBOutlet weak var centerY: UILabel!
    @IBOutlet weak var rotation: UILabel!

    // Sliders
    @IBOutlet weak var frameXSlider: UISlider!
    @IBOutlet weak var frameYSlider: UISlider!
    @IBOutlet weak var frameWidthSlider: UISlider!
    @IBOutlet weak var frameHeightSlider: UISlider!
    @IBOutlet weak var boundsXSlider: UISlider!
    @IBOutlet weak var boundsYSlider: UISlider!
    @IBOutlet weak var boundsWidthSlider: UISlider!
    @IBOutlet weak var boundsHeightSlider: UISlider!
    @IBOutlet weak var centerXSlider: UISlider!
    @IBOutlet weak var centerYSlider: UISlider!
    @IBOutlet weak var rotationSlider: UISlider!

    // Slider actions
    @IBAction func frameXSliderChanged(sender: AnyObject) {
        myView.frame.origin.x = CGFloat(frameXSlider.value)
        updateLabels()
    }
    @IBAction func frameYSliderChanged(sender: AnyObject) {
        myView.frame.origin.y = CGFloat(frameYSlider.value)
        updateLabels()
    }
    @IBAction func frameWidthSliderChanged(sender: AnyObject) {
        myView.frame.size.width = CGFloat(frameWidthSlider.value)
        updateLabels()
    }
    @IBAction func frameHeightSliderChanged(sender: AnyObject) {
        myView.frame.size.height = CGFloat(frameHeightSlider.value)
        updateLabels()
    }
    @IBAction func boundsXSliderChanged(sender: AnyObject) {
        myView.bounds.origin.x = CGFloat(boundsXSlider.value)
        updateLabels()
    }
    @IBAction func boundsYSliderChanged(sender: AnyObject) {
        myView.bounds.origin.y = CGFloat(boundsYSlider.value)
        updateLabels()
    }
    @IBAction func boundsWidthSliderChanged(sender: AnyObject) {
        myView.bounds.size.width = CGFloat(boundsWidthSlider.value)
        updateLabels()
    }
    @IBAction func boundsHeightSliderChanged(sender: AnyObject) {
        myView.bounds.size.height = CGFloat(boundsHeightSlider.value)
        updateLabels()
    }
    @IBAction func centerXSliderChanged(sender: AnyObject) {
        myView.center.x = CGFloat(centerXSlider.value)
        updateLabels()
    }
    @IBAction func centerYSliderChanged(sender: AnyObject) {
        myView.center.y = CGFloat(centerYSlider.value)
        updateLabels()
    }
    @IBAction func rotationSliderChanged(sender: AnyObject) {
        let rotation = CGAffineTransform(rotationAngle: CGFloat(rotationSlider.value))
        myView.transform = rotation
        updateLabels()
    }

    private func updateLabels() {

        frameX.text = "frame x = \(Int(myView.frame.origin.x))"
        frameY.text = "frame y = \(Int(myView.frame.origin.y))"
        frameWidth.text = "frame width = \(Int(myView.frame.width))"
        frameHeight.text = "frame height = \(Int(myView.frame.height))"
        boundsX.text = "bounds x = \(Int(myView.bounds.origin.x))"
        boundsY.text = "bounds y = \(Int(myView.bounds.origin.y))"
        boundsWidth.text = "bounds width = \(Int(myView.bounds.width))"
        boundsHeight.text = "bounds height = \(Int(myView.bounds.height))"
        centerX.text = "center x = \(Int(myView.center.x))"
        centerY.text = "center y = \(Int(myView.center.y))"
        rotation.text = "rotation = \((rotationSlider.value))"

    }

}
Suragch
источник
Выглядит так, как будто ширина и высота границы избыточны, и свойства «origin» было бы достаточно (как это было с Flash).
Ян Уорбертон
используйте границы для «как рисование вещей или организация подпредставлений в представлении». Вы имеете в виду, если у меня есть viewController, который имеет две кнопки, я должен разместить кнопки, используя «границы» представления viewController ?! Я всегда использовал кадр представления ...
Дорогой
@Мед, я немного устала от своих навыков в iOS, поскольку работала над Android в течение последнего года. Я считаю, что корневой вид View Controller заполняет экран, поэтому его рамка и границы должны быть одинаковыми.
Сурагч
5
Созданный github.com/maniramezan/FrameVsBounds.git Repo на основе примера приложения данного здесь , чтобы помочь лучше понять boundsпротив frameиз вида.
человек
1
@IanWarburton Когда вид поворачивается, ширина и высота границы не совпадают с рамкой, и поэтому не являются избыточными.
Эдвард Брей
43

попробуйте запустить код ниже

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
    UIView *v = [w.subviews objectAtIndex:0];

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}

вывод этого кода:

ориентация устройства корпуса - книжная

{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}

ориентация устройства корпуса - альбомная

{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}

очевидно, вы можете увидеть разницу между рамкой и границами

Тристан
источник
2
это больше не правда (iOS 8)
Julian Król
1
stackoverflow.com/questions/24150359/…
Джулиан Крол
13

Рама представляет собой прямоугольник , который определяет UIView с относительно его надтаблицы .

В границах Прямоугольника является диапазоном значений , которые определяют , что NSView системы координат.

то есть все в этом прямоугольнике будет отображаться в UIView.

Jorik
источник
10

рамка - это начало координат (верхний левый угол) и размер представления в системе координат его суперпредставления, это означает, что вы переводите представление в его суперпредставление, изменяя начало координат рамки, с другой стороны , границы это размер и начало координат собственная система координат, поэтому по умолчанию начало координат равно (0,0).

Большую часть времени кадр и границы являются конгруэнтными, но если у вас есть представление о кадре ((140,65), (200,250)) и границах ((0,0), (200,250)), например, и представление было наклонено так что он стоит в своем нижнем правом углу, тогда границы все равно будут ((0,0), (200,250)), а рамка - нет.

введите описание изображения здесь

рамка будет наименьшим прямоугольником, который инкапсулирует / окружает вид, поэтому рамка (как на фотографии) будет ((140,65), (320,320)).

другое отличие, например, если у вас есть суперпоказ, границы которого ((0,0), (200,200)), и у этого суперпоказа есть вспомогательный вид, фрейм которого ((20,20), (100,100)), и вы изменили границы супервизора в ((20,20), (200,200)), тогда кадр subView будет все еще ((20,20), (100,100)), но смещен на (20,20), потому что его система координат суперпредставления была смещена (20, 20).

Надеюсь, это кому-нибудь поможет.

m.eldehairy
источник
Относительно последнего параграфа: subView фрейм относительно этого superView. изменение superViewграниц ни к чему не приведет к тому, что subViewпроисхождение совпадет с этим superView.
staticVoidMan
5

Кадрируйте его относительно своего SuperView, тогда как Bounds относительно его NSView.

Пример: X = 40, Y = 60. Также содержит 3 вида. Эта диаграмма показывает вам ясную идею.

РАМКА

BOUNDS

Рагхунатхи
источник
4

Позвольте мне добавить мои 5 центов.

Frame используется родительским представлением представления, чтобы поместить его в родительское представление.

Границы используются самим представлением для размещения собственного содержимого (как при прокрутке во время прокрутки). Смотрите также clipsToBounds . Границы также могут быть использованы для увеличения / уменьшения содержимого представления.

Аналогия:
Рамка ~ Экран телевизора
Границы ~ Камера (масштабирование, перемещение, поворот)

Serg
источник
2

Ответы выше очень хорошо объяснили разницу между границами и фреймами.

Границы: Размер и расположение вида согласно его собственной системе координат.

Кадр: размер представления и расположение относительно его SuperView.

Тогда возникает путаница, что в случае границ X, Y всегда будет "0". Это не верно . Это можно понять в UIScrollView и UICollectionView.

Когда границы x, y не равны 0.
Предположим, у нас есть UIScrollView. Мы осуществили нумерацию страниц. UIScrollView имеет 3 страницы, а ширина его ContentSize в три раза больше ширины экрана (предположим, что ширина экрана равна 320). Высота постоянна (предположим, 200).

scrollView.contentSize = CGSize(x:320*3, y : 200)

Добавьте три UIImageViews как subViews и внимательно посмотрите на значение x кадра

let imageView0 = UIImageView.init(frame: CGRect(x:0, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView1 :  UIImageView.init( frame: CGRect(x:320, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView2 :  UIImageView.init(frame: CGRect(x:640, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
  1. Страница 0: Когда ScrollView находится на странице 0, границы будут (x: 0, y: 0, ширина: 320, высота: 200)

  2. Страница 1: Прокрутите страницу и перейдите на страницу 1.
    Теперь границы будут (x: 320, y: 0, ширина: 320, высота: 200). Помните, что мы говорили относительно своей собственной системы координат. Так что теперь «Видимая часть» нашего ScrollView имеет «x» на 320. Посмотрите на кадр imageView1.

  3. Страница 2: Прокрутите страницу и перейдите на страницу 2 Границы: (x: 640, y: 0, ширина: 320, высота: 200) Снова взгляните на кадр imageView2

То же самое для случая UICollectionView. Самый простой способ взглянуть на collectionView - это прокрутить его и распечатать / записать его границы, и вы получите идею.

Мухаммед Наяб
источник
2

Все ответы выше верны, и это мое мнение об этом:

Чтобы различать рамку и границы, КОНЦЕПЦИИ разработчик должен прочитать:

  1. относительно суперпредставления (одно родительское представление) оно содержится в = FRAME
  2. относительно своей собственной системы координат, определяет ее местоположение подвид = BOUNDS

«границы» сбивают с толку, потому что создается впечатление, что координаты - это позиция вида, для которого он установлен. Но они находятся в отношениях и корректируются в соответствии с константами кадра.

экран iPhone

Марина
источник
1

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

  • View B это подвид View A
  • View B был перемещен в x:60, y: 20
  • View B был повернут 45 degrees

введите описание изображения здесь

yoAlex5
источник
0

Рамка против границы

  • Если вы создаете вид в X: 0, Y: 0, ширина: 400, высота: 400, его рамка и границы совпадают.
  • Если вы переместите это представление в X: 400, его кадр будет отражать это изменение, но его границы не будут. Помните, что границы относятся к собственному пространству представления, а внутри представления ничего не изменилось.
  • Если вы преобразуете вид, например, поворачиваете его или масштабируете, рамка изменится, чтобы отразить это, но границы все равно не изменятся - что касается вида внутри, он не изменился.
  • Если вы измените границы, то это изменит содержимое внутри фрейма, потому что начало прямоугольника границ начинается в другой части представления.
Анит Кумар
источник
-1

frame = расположение и размер представления, используя систему координат родительского представления

bounds = расположение и размер представления с использованием его собственной системы координат

Представление отслеживает свой размер и местоположение, используя два прямоугольника: прямоугольник рамки и прямоугольник границ. Прямоугольник рамки определяет местоположение и размер представления в суперпредставлении, используя систему координат суперпредставления. Прямоугольник границ определяет внутреннюю систему координат, которая используется при рисовании содержимого вида, включая начало координат и масштабирование. На рисунке 2-1 показано соотношение между прямоугольником рамки слева и прямоугольником границ справа ».

Короче говоря, фрейм - это представление супервьюма о представлении, а границы - это собственная идея представления. Наличие нескольких систем координат, по одной для каждого вида, является частью иерархии видов.

Хаб Ион
источник
Где находится рисунок 2-1?
Александр Маяцкий