Какао: Какая разница между рамкой и границами? - PullRequest
551 голосов
/ 31 июля 2009

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

Ответы [ 9 ]

862 голосов
/ 31 июля 2009

границы UIView - это прямоугольник , выраженный в виде местоположения (x, y) и размера (ширина, высота) относительно его собственного система координат (0,0).

кадр UIView - это прямоугольник , выраженный в виде местоположения (x, y) и размера (ширина, высота) относительно суперпредставления оно содержится внутри.

Итак, представьте себе представление, которое имеет размер 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, которые не только объясняют разницу между рамками и границами, но также показывают наглядные примеры.

587 голосов
/ 07 марта 2015

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

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

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

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

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

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

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

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

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

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

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

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

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

enter image description here

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

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

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

enter image description here

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

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

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

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

enter image description here

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

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

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

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

enter image description here

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

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

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

enter image description here

Рамка не перемещалась в суперпредставлении, но содержимое внутри рамки изменилось, потому что начало прямоугольника границ начинается в другой части представления. В этом вся идея UIScrollView и его подклассов (например, UITableView). См. Понимание UIScrollView для получения дополнительной информации.

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

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

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

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

Apple Docs

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

Другие ресурсы

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

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

enter image description here

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

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))"

    }

}
43 голосов
/ 04 января 2013

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

- (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}}

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

13 голосов
/ 23 мая 2012

frame - это прямоугольник, который определяет UIView с относительно его суперпредставления .

Прямоугольник bounds - это диапазон значений, которые определяют эту систему координат NSView.

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

9 голосов
/ 08 апреля 2015

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

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

enter image description here

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

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

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

4 голосов
/ 17 декабря 2015

Кадр относительно его SuperView, тогда как границы относительно его NSView.

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

FRAME

BOUNDS

1 голос
/ 17 апреля 2019

Все ответы выше верны, и это мое мнение по этому поводу:

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

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

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

iPhone screen

1 голос
/ 07 июня 2018

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

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

Рамка: размер и расположение вида относительно его 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 - это прокрутить его и распечатать / записать его границы, и вы поймете, как это сделать.

0 голосов
/ 24 июня 2019

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

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

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

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

...