Какао: Яка різниця між рамкою та межею?


587

UIViewі його підкласи мають усі властивості frameі bounds. Яка різниця?


1
Розуміння кадру macoscope.com/blog/understanding-frame
onmyway133

для мене найкоротше
Влад

1
У відео о 2:30 немає нічого стислого. Я можу прочитати речення чи два набагато швидше, ніж 2:30.
dsjoerg

Для мене набагато зрозуміліше, якщо я думаю так: Кадр = Межі та позиція
Serg

Відповіді:


902

У межах А. Н. UIView є прямокутником , вираженим як місце розташування (х, у) і розмір (ширина, висота) по відношенню до своєї власної системи координат (0,0).

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

Отже, уявіть вигляд, який має розмір 100x100 (ширина х висота), розміщений на рівні 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: для переміщення / перекладу джерела. Для отримання додаткової інформації див. "Перегляд геометрії" в "Посібнику з програмування какао".
Мелтемi

127
UIScrollView - приклад, коли межі можуть зміщуватися, щоб відображати різні області вмісту в представленні.
Бред Ларсон

92
Невелика підказка - використання NSStringFromCGRect може заощадити деякий час для реєстрації ректів.
берилій

645

Коротка відповідь

frame = розташування та розмір подання за допомогою системи координат батьківського перегляду

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

межі = розташування та розмір подання, використовуючи власну систему координат

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

Детальний відповідь

Щоб мені запам'ятати кадр , я думаю про рамку для картин на стіні . Рамка для малюнка схожа на межу перегляду. Я можу повісити малюнок де завгодно на стіну. Таким же чином я можу поставити представлення будь-де, де я хочу всередині батьківського подання (його також називають наглядом). Батьківський погляд - як стіна. Походження системи координат в 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))"

    }

}

Схоже, ширина та висота обмеженої лінії зайві, а властивості "origin" було б достатньо (як це було у Flash).
Ian Warburton

використовувати межі для "як малювання речей або впорядкування підвидів у межах подання". ви маєте на увазі, якщо у мене є viewController, який має дві кнопки, я повинен розміщувати кнопки, використовуючи «межі» подання viewController ?! Я завжди використовував кадр думки ...
Мед

@Honey, я трохи іржавий на своїх навичках iOS, оскільки останній рік працюю над Android. Я вважаю, що кореневий вигляд контролера перегляду заповнює екран, тому його кадр та межі повинні бути однаковими.
Сурагч

5
Створено github.com/maniramezan/FrameVsBounds.git repo на основі прикладу програми, наведеної тут, щоб допомогти краще зрозуміти boundsпроти frameперегляду.
manman

Дякую! Гарне пояснення. Зображення цінніше 1000 слів
Педро Трухільо

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


13

Рама являє собою прямокутник , який визначає UIView з відносно його надтабліци .

В межах Прямокутника є діапазоном значень , які визначають , що NSView системи координат.

тобто все в цьому прямокутнику буде фактично відображатися в UIView.


10

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

більшу частину часу кадри та межі є конгруентними, але якщо у вас є вид кадру ((140,65), (200,250)) та меж ((0,0), (200,250)), наприклад, і вигляд був нахилений так що він стоїть у нижньому правому куті, то рамки все одно будуть ((0,0), (200,250)), але рамка - ні.

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

кадр буде найменшим прямокутником, який інкапсулює / оточує вигляд, тому кадр (як на фото) буде ((140,65), (320,320)).

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

Я сподіваюся, що це комусь допоможе.


щодо останнього абзацу: subView кадр відносно нього superView. зміна superViewмеж нічого не призведе до того, що subViewпоходження збігається з цим superView.
staticVoidMan

5

Кадр відносно його SuperView, тоді як Межі відносно його NSView.

Приклад: X = 40, Y = 60. Також містить 3 перегляди. Ця діаграма показує вам чітке уявлення.

РАМКА

ОБЛАДКИ


4

Дозвольте додати свої 5 центів.

Кадр використовується батьківським поданням подання, щоб розмістити його всередині батьківського подання.

Межі використовується самим представленням для розміщення власного вмісту (як це робить перегляд прокрутки під час прокрутки). Дивіться також кліпиToBounds . Межі також можна використовувати для збільшення / зменшення вмісту перегляду.

Аналогія:
Кадр ~
Зв'язок екрану телевізора ~ Камера (збільшення, переміщення, обертання)


2

Наведені вище відповіді дуже добре пояснили різницю між Рамками та Рамками.

Межі: Розмір і розташування перегляду відповідно до власної системи координат.

Кадр: розмір перегляду та розташування щодо його SuperView.

Тоді виникає плутанина, що у випадку Bounds X, Y завжди буде "0". Це неправда . Це можна зрозуміти також у UIScrollView та UICollectionView.

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

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

Додайте три UIImageView як підпогляди та пильно подивіться на значення 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.
    Тепер межі будуть (х: 320, у: 0, ширина: 320, висота: 200). Пам'ятайте, що ми говорили стосовно власної системи координат. Отже, "Видима частина" нашого ScrollView має "x" на 320. Подивіться на кадр imageView1.

  3. Сторінка 2: Прокрутіть і перейдіть до сторінки 2 Рамки: (x: 640, y: 0, ширина: 320, висота: 200) Знову подивіться на кадр imageView2

Те саме для випадку UICollectionView. Найпростіший спосіб поглянути на collectionView - прокрутити його та надрукувати / записати його межі, і ви отримаєте ідею.


2

Усі відповіді, наведені вище, є правильними, і я вважаю, що це:

Для розмежування кадру та меж CONCEPTS розробник повинен прочитати:

  1. по відношенню до огляду (один батьківський погляд) він міститься в межах = FRAME
  2. відносно власної системи координат визначає його місце підпогляду = BOUNDS

"межі" є заплутаними, оскільки створюється враження, що координати - це положення виду, для якого воно встановлено. Але вони співвідносяться і регулюються відповідно до констант кадру.

Екран iPhone


1

Ще одна ілюстрація, щоб показати різницю між рамкою та межею. На цьому прикладі:

  • View B є підглядом View A
  • View B було переміщено до x:60, y: 20
  • View B був повернутий 45 degrees

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


0

Кадр проти зв'язаного

  • Якщо ви створюєте подання на X: 0, Y: 0, ширина: 400, висота: 400, його рамка та межі однакові.
  • Якщо перенести цей погляд на X: 400, його кадр буде відображати цю зміну, але його межі не будуть. Пам'ятайте, що межі відносяться до власного простору подання, і всередині цього виду нічого не змінилося.
  • Якщо ви трансформуєте подання, наприклад, обертаючи його чи збільшуючи його вгору, кадр зміниться, щоб відобразити це, але межі все одно не будуть - що стосується представлення внутрішнього вигляду, він не змінився.
  • Якщо ви зміните межі, то це змінить вміст всередині кадру, оскільки початок прямокутника меж починається в іншій частині подання.

-1

frame = розташування та розмір подання за допомогою системи координат батьківського перегляду

межі = розташування та розмір подання, використовуючи власну систему координат

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

Коротше кажучи, кадр - це ідея перегляду виду, а межі - це власна ідея перегляду. Наявність декількох систем координат, по одній для кожного перегляду, є частиною ієрархії подання.


Де рисунок 2-1?
Олександр Маяцький
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.