Рамка UIView, границы и центр

Я хотел бы знать, как правильно использовать эти свойства.

Как я понимаю, frame может использоваться из контейнера создаваемого представления. Он устанавливает положение представления относительно вида контейнера. Он также устанавливает размер этого вида.

Также center может использоваться из контейнера создаваемого представления. Это свойство изменяет положение представления относительно его контейнера.

Наконец, bounds относится к самому представлению. Он изменяет область вывода для представления.

Можете ли вы рассказать подробнее о связи между frame и bounds? Как насчет свойств clipsToBounds и masksToBounds?

Ответ 1

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

Сначала перескажите вопрос: кадр, границы и центр и их отношения.

Рамка Вид frame (CGRect) - это положение его прямоугольника в системе координат superview. По умолчанию он начинается в левом верхнем углу.

Границы Вид bounds (CGRect) выражает прямоугольник представления в своей собственной системе координат.

Центр A center - это CGPoint, выраженный в терминах системы координат superview и определяет положение точной центральной точки вида.

Взято из UIView + position, это отношения (они не работают в коде, поскольку они являются неформальными уравнения) среди предыдущих свойств:

  • frame.origin = center - (bounds.size / 2.0)

  • center = frame.origin + (bounds.size / 2.0)

  • frame.size = bounds.size

ПРИМЕЧАНИЕ. Эти отношения не применяются, если виды повернуты. Для получения дополнительной информации я предложу вам взглянуть на следующее изображение, взятое из Кухонный ящик на основе курс Stanford CS193p. Кредиты относятся к @Rhubarb.

Frame, bounds and center

Использование frame позволяет вам переместить и/или изменить размер представления в пределах superview. Обычно можно использовать из superview, например, при создании определенного подвью. Например:

// view1 will be positioned at x = 30, y = 20 starting the top left corner of [self view]
// [self view] could be the view managed by a UIViewController
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)];    
view1.backgroundColor = [UIColor redColor];

[[self view] addSubview:view1];

Если вам нужны координаты для рисования внутри view, вы обычно ссылаетесь на bounds. Типичным примером может быть рисование внутри view подвид в качестве вставки первого. Для рисования подвью нужно знать bounds супервизора. Например:

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 400.0f, 400.0f)];    
view1.backgroundColor = [UIColor redColor];

UIView* view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];    
view2.backgroundColor = [UIColor yellowColor];

[view1 addSubview:view2];

Различные изменения происходят, когда вы меняете bounds представления. Например, если вы измените bounds size, frame изменится (и наоборот). Это изменение происходит вокруг center представления. Используйте приведенный ниже код и посмотрите, что произойдет:

NSLog(@"Old Frame %@", NSStringFromCGRect(view2.frame));
NSLog(@"Old Center %@", NSStringFromCGPoint(view2.center));    

CGRect frame = view2.bounds;
frame.size.height += 20.0f;
frame.size.width += 20.0f;
view2.bounds = frame;

NSLog(@"New Frame %@", NSStringFromCGRect(view2.frame));
NSLog(@"New Center %@", NSStringFromCGPoint(view2.center));

Кроме того, если вы измените bounds origin, вы измените origin своей внутренней системы координат. По умолчанию origin находится в (0.0, 0.0) (верхний левый угол). Например, если вы измените origin для view1, вы можете увидеть (если хотите, комментарий предыдущего кода), то теперь верхний левый угол для view2 касается view1. Мотивация довольно проста. Вы скажете view1, что его верхний левый угол теперь находится в позиции (20.0, 20.0), но поскольку view2 frame origin начинается с (20.0, 20.0), они будут совпадать.

CGRect frame = view1.bounds;
frame.origin.x += 20.0f;
frame.origin.y += 20.0f;
view1.bounds = frame; 

origin представляет позицию view внутри своего superview, но описывает положение центра bounds.

Наконец, bounds и origin не являются связанными понятиями. Оба позволяют получить frame представления (см. Предыдущие уравнения).

Рассмотрение примера1 View1

Вот что происходит при использовании следующего фрагмента.

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)];
view1.backgroundColor = [UIColor redColor];

[[self view] addSubview:view1];

NSLog(@"view1 frame is: %@", NSStringFromCGRect([view1 frame]));
NSLog(@"view1 bounds is: %@", NSStringFromCGRect([view1 bounds]));
NSLog(@"view1 center is: %@", NSStringFromCGPoint([view1 center]));

Относительный образ.

enter image description here

Это вместо того, что произойдет, если я изменю [self view] границы, как показано ниже.

// previous code here...
CGRect rect = [[self view] bounds];
rect.origin.x += 30.0f;
rect.origin.y += 20.0f;
[[self view] setBounds:rect];

Относительный образ.

enter image description here

Здесь вы говорите [self view], что его верхний левый угол теперь находится в позиции (30.0, 20.0), но поскольку начало отсчета view1 начинается с (30.0, 20.0), они будут совпадать.

Дополнительные ссылки (для обновления с помощью других ссылок, если вы хотите)

О clipsToBounds (исходный документ Apple)

Установка этого значения в значение YES приводит к тому, что subviews будут обрезаны до границ приемника. Если установлено значение NO, subviews, чьи рамки выходят за пределы видимые границы приемника не обрезаны. Значение по умолчанию НЕТ.

Другими словами, если представление frame равно (0, 0, 100, 100), а его подвью (90, 90, 30, 30), вы увидите только часть этого поднабора. Последнее не будет превышать границы родительского вида.

masksToBounds эквивалентен clipsToBounds. Вместо UIView это свойство применяется к CALayer. Под капотом clipsToBounds вызывается masksToBounds. Для дальнейших ссылок взгляните на Какова связь между клипами UIViewToBounds и масками CALayerToBounds?.

Ответ 2

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

Чтобы помочь мне запомнить фрейм, я думаю о рамке на стене. Подобно тому, как изображение можно перемещать в любом месте стены, система координат рамы просмотра - это супервизор. (wall = superview, frame = view)

Чтобы помочь мне запомнить границы, я думаю о пределах баскетбольной площадки. Баскетбол находится где-то внутри суда, точно так же, как система координат точек зрения находится внутри самого представления. (court = view, basketball/players = content внутри представления)

Подобно кадру, view.center также находится в координатах надстройки.

Рамка против границ - Пример 1

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

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

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

enter image description here


Пример 2

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


Пример 3

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


Пример 4

Это то же самое, что и в примере 2, за исключением того, что на этот раз отображается весь контент представления, как бы он выглядел, если бы он не был привязан к границам представления.

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

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

enter image description here


Пример 5

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

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

enter image description here

Снова, см. здесь для получения более подробной информации.

Ответ 3

Я нашел этот образ наиболее полезным для понимания фрейма, границ и т.д.

enter image description here

Также обратите внимание, что frame.size != bounds.size, когда изображение повернуто.

Ответ 4

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

Ответ 5

Я думаю, если вы думаете, что это с точки зрения CALayer, все становится понятным.

Кадр на самом деле не является отдельным свойством представления или слоя вообще, это виртуальное свойство, вычисленное из границ, положения (UIView center) и преобразуется.

Итак, в основном, как это делают свойства layer/view, эти три свойства (и anchorPoint), и любое из этих трех свойств не изменит никакого другого свойства, как изменение преобразования не изменяет границ.

Ответ 6

Есть очень хорошие ответы с подробным объяснением этого сообщения. Я просто хотел бы напомнить, что есть еще одно объяснение с визуальным представлением для значения Frame, Bounds, Center, Transform, Bounds Origin в WWDC 2011 video Понимание рендеринга UIKit, начиная с @4: 22 до 20:10

Ответ 7

Прочитав приведенные выше ответы, добавьте мои интерпретации.

Предположим, что просмотр в Интернете, веб-браузер - это ваш frame, который решает, где и как большой показать веб-страницу. Скроллер браузера - это ваш bounds.origin, который определяет, какая часть веб-страницы будет показана. bounds.origin трудно понять. Лучший способ узнать - создать приложение Single View, попробовать изменить эти параметры и посмотреть, как изменяются субвью.

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100.0f, 200.0f, 200.0f, 400.0f)];
[view1 setBackgroundColor:[UIColor redColor]];

UIView *view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];
[view2 setBackgroundColor:[UIColor yellowColor]];
[view1 addSubview:view2];

[[self view] addSubview:view1];

NSLog(@"Old view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center));
NSLog(@"Old view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));

// Modify this part.
CGRect bounds = view1.bounds;
bounds.origin.x += 10.0f;
bounds.origin.y += 10.0f;

// incase you need width, height
//bounds.size.height += 20.0f;
//bounds.size.width += 20.0f;

view1.bounds = bounds;

NSLog(@"New view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center));
NSLog(@"New view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));