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

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

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

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

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

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


person Lorenzo B    schedule 19.03.2011    source источник
comment
Это обсуждение уже решено здесь. поэтому, пожалуйста, посмотрите это здесь или можете увидеть документацию для разработчиков, указанную здесь developer.apple.com/library/ios/#documentation/uikit/reference/ stackoverflow.com/questions/1210047/ slideshare.net/onoaonoa/cs193p-lecture-5-view-animation   -  person Splendid    schedule 19.03.2011


Ответы (6)


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

Сначала резюмируем вопрос: фрейм, границы, центр и их отношения.

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

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

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

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

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

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

  • frame.size = bounds.size

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

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

Использование 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), но поскольку frame origin view2 начинается с (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 представления (см. Предыдущие уравнения).

Пример использования 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's frame is: %@", NSStringFromCGRect([view1 frame]));
NSLog(@"view1's bounds is: %@", NSStringFromCGRect([view1 bounds]));
NSLog(@"view1's center is: %@", NSStringFromCGPoint([view1 center]));

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

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

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

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

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

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

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

Дополнительные ссылки (при желании можно добавить другие ссылки)

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

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

Другими словами, если frame представления - (0, 0, 100, 100), а его подпредставление - (90, 90, 30, 30), вы увидите только часть этого подпредставления. Последний не выходит за пределы родительского представления.

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

person Community    schedule 01.07.2012
comment
@Rhubarb Вы совершенно правы. Я укажу на это в своем ответе. Спасибо. - person Lorenzo B; 05.12.2012
comment
Я не знаю, получу ли я ответ, но почему подпредставление движется в отрицательном направлении при изменении начала границ. Просто не могу осмыслить это. Спасибо!! - person nimgrg; 20.06.2013
comment
@nimgrg Кажется, что взгляд движется в отрицательном направлении, но это не так. Изменены внутренние координаты супервизора. - person Lorenzo B; 20.06.2013
comment
простите мои геометрические способности, если внутренние координаты супервизора изменяются на начало координат (30.0, 20.0), точка (0,0) находится внутри синего квадрата или за его пределами. Я просто пытаюсь отследить изменения и разобраться в них. Спасибо, что нашли время ответить. - person nimgrg; 20.06.2013
comment
@flexaddicted: Привет, добавленный вами слайд говорит: Середина представления B в его собственном координатном пространстве: bounds.size.width / 2 + origin.x - почему это так? Поскольку заголовок говорит в собственном координатном пространстве, я бы сказал, что это: bounds.size.width / 2 + bounds.origin.x ?? Не правда ли ?? - person ; 06.08.2013
comment
@ user2568508 Да, на самом деле так и есть. ;) - person Lorenzo B; 06.08.2013
comment
@flexaddicted: хм теперь немного запутался, кажется, на слайде написано bounds.size.width / 2 + bounds.origin.x - по некоторым причинам кажется, что я не заметил bounds перед origin.x и, следовательно, мой комментарий. Кажется, правильный ответ: .. + bounds.origin.x - person ; 06.08.2013
comment
На первом изображении, как я могу определить, когда CGPoint (например, 141,66) находится внутри кадра, но не в области границ? И наоборот? - person Xiangdong; 22.01.2015
comment
Разработчики в основном ограничивают представления, используя 1) раскадровки (хорошо) 2) ограничения (хорошо) 3) фрейм супервизора (не хорошо) 4) Я редко вижу, чтобы они использовали границы супервизора. Итак, в заключение вы говорите, что использование кадра супервизора не имеет смысла, потому что представление может быть повернуто, и вместо этого границы супервизора - правильный подход. В этом есть смысл, но я думаю, поскольку большую часть времени мы не имеем дело с повернутыми представлениями, мы не сталкиваемся с неожиданными проблемами. Верно? В: Есть ли случаи, когда на самом деле использование границ супервизора было бы не тем, что вам нужно? Если да, то когда? - person Honey; 07.10.2018

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

Чтобы помочь мне запомнить рамку, я думаю о рамке на стене. Точно так же, как изображение можно переместить в любое место на стене, система координат рамки вида - это супервизор. (стена = супервизор, рамка = вид)

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

Как и фрейм, view.center также находится в координатах супервизора.

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

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

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

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

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


Пример 2

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

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

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


Пример 3

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

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

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


Пример 4

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

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

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

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


Пример 5

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

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

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

Опять же, мой ответ с более подробной информацией см. здесь.

person Suragch    schedule 08.03.2015
comment
Спасибо, Сурагч. Это действительно очень четкая картина разницы между рамками и границами. Я очень ценю твои старания. - person Mohd Haider; 26.03.2015
comment
Все, что я хочу увидеть в одном коротком и лаконичном ответе. Спасибо! - person Matt Chuang; 27.01.2016
comment
Потрясающие! Например, 3: я не понимаю. вы только немного переместили исходную точку кадра, как это повлияет на поворот изображения? - person Honey; 16.04.2016
comment
@ asma22, В примере 3 я просто пытался показать, что при повороте изображения рамка меняется, а границы - нет. См. мой более полный ответ. - person Suragch; 16.04.2016

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

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

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

person Erben Mo    schedule 12.10.2012
comment
Картина стоит тысячи слов. - person Narendra Kamma; 20.11.2012
comment
Лучшее возможное объяснение - person Ratikanta Patra; 30.05.2013
comment
@Erben Mo: Привет, слайд, который вы добавили, говорит: Середина представления B в его собственном координатном пространстве: bounds.size.width / 2 + origin.x - почему это так? Поскольку заголовок говорит в собственном координатном пространстве, я бы сказал, что это: bounds.size.width / 2 + bounds.origin.x ?? Не правда ли ?? - person ; 06.08.2013
comment
Каков источник этого изображения? Ссылка? - person David; 15.10.2014
comment
@David Это из Стэнфордского курса CS193p на iTunesU, который можно найти здесь: www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2013-fall - person preynolds; 21.08.2015

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

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

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

person coolbeet    schedule 21.10.2014

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

person Wael Showair    schedule 31.10.2015

Прочитав приведенные выше ответы, я добавляю свои интерпретации.

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

- (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));
person NSTNF    schedule 03.04.2015