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

StackOverflow https://stackoverflow.com/questions/1210047

  •  06-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

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

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

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

Чтобы помочь мне вспомнить рамка, Я думаю о рамка для фотографий на стене.Рамка изображения подобна границе обзора.Я могу повесить картину на стену где захочу.Точно так же я могу поместить представление в любое место внутри родительского представления (также называемого суперпредставлением).Родительский вид похож на стену.Начало системы координат в 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 градусов по часовой стрелке.(Вращение осуществляется с помощью преобразований.Посмотрите документация и эти вид и примеры слоев Чтобы получить больше информации.)

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

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

В документах 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

Связанные вопросы по 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))"

    }

}

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

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

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

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

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

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

frame - это начало координат (верхний левый угол) и размер представления в системе координат его суперпредставления, это означает, что вы переводите представление в его суперпредставлении путем изменения источника кадра, bounds - это размер и начало координат в собственной системе координат, поэтому по умолчанию начало координат равно (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)), и вы изменили superView ограничивается ((20,20), (200,200)), тогда кадр subView будет все еще ((20,20), (100,100)), но смещен на (20,20), потому что его система координат суперпредставления была смещена (20,20).

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

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

Пример: X = 40, Y = 60. Также содержит 3 вида. На этой диаграмме показано четкое представление.

FRAME

BOUNDS

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

  

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

     

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

Тогда возникает путаница, что в случае Bounds 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)
<Ол>
  • Страница 0: Когда ScrollView на 0 странице, границы будут (x: 0, y: 0, ширина: 320, высота: 200)

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

  • Страница 2: Прокрутите и перейдите на страницу 2 Границы: (x: 640, y: 0, ширина: 320, высота: 200) Снова взгляните на кадр imageView2
  • То же самое для случая UICollectionView. Самый простой способ взглянуть на collectionView - это прокрутить его и распечатать / записать его границы, и вы поймете идею.

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

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

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

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

    iPhone screen

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

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top