Какао: Какая разница между рамкой и границами?
UIView
и его подклассы все имеют свойства frame
а также bounds
, Какая разница?
14 ответов
Границами 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, которые не только объясняют разницу между рамками и границами, но также показывают наглядные примеры.
Короткий ответ
frame = расположение и размер представления с использованием системы координат родительского представления
- Важно для: размещения представления в родительском
bounds = расположение и размер представления с использованием его собственной системы координат
- Важно для: размещения содержимого представления или подпредставлений внутри себя
Подробный ответ
Чтобы помочь мне вспомнить рамку, я думаю о картинной рамке на стене. Рамка рисунка похожа на границу вида. Я могу повесить картину где угодно на стене. Таким же образом я могу поместить представление в любом месте внутри родительского представления (также называемого суперпредставлением). Родительский вид похож на стену. Начало системы координат в 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
свойство не содержит преобразования идентификаторов, кадр этого представления не определен, как и результаты его автоматического изменения размеров.
Скорее неудачно об авторазмере... Есть кое-что, что вы можете сделать, хотя.
При изменении
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
Кадр не перемещался в суперпредставлении, но содержимое внутри кадра изменилось, потому что начало прямоугольника границ начинается в другой части представления. Это вся идея UIScrollView
и это подклассы (например, UITableView
). Посмотрите Понимание UIScrollView для большего объяснения.
Когда использовать рамку и когда использовать границы
поскольку frame
Относится к местоположению представления в его родительском представлении, вы используете его при внесении внешних изменений, таких как изменение его ширины или нахождение расстояния между представлением и вершиной его родительского представления.
Использовать bounds
когда вы делаете внутренние изменения, такие как рисование вещей или организация подпредставлений в представлении. Также используйте границы, чтобы получить размер вида, если вы произвели некоторую трансформацию на нем.
Статьи для дальнейшего исследования:
Apple документы
Связанные вопросы Stackru
- UIView рамка, границы и центр
- Рамка UIView, границы, центр, происхождение, когда использовать что?
- "Неправильный" размер рамки / окна после переориентации в iPhone
Другие источники
- Вы, вероятно, не понимаете рамки и границы
- Основы iOS: рамки, границы и CGGeometry
- CS193p Лекция 5 - Виды, Рисование, Анимация
Практикуй себя
В дополнение к чтению вышеупомянутых статей, мне очень помогает сделать тестовое приложение. Возможно, вы захотите попробовать сделать что-то подобное. (Я получил идею из этого видео курса, но, к сожалению, это не бесплатно.)
Вот код для вашей справки:
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.
Еще одна иллюстрация, показывающая разницу между рамкой и границами. В этом примере:
View B
это частьView A
View B
был перемещен вx:60, y: 20
View B
был повернут45 degrees
https:https://stackru.com/images/7161bdc793032f63030650fa83b4e515f4c6f906.png
рамка - это начало координат (верхний левый угол) и размер вида в системе координат его суперпредставления, это означает, что вы переводите представление в его суперпредставление, изменяя начало кадра, с другой стороны, границы - это размер и начало координат в его собственная система координат, поэтому по умолчанию начало координат равно (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)), и вы изменили границы супервизора в ((20,20), (200,200)), тогда кадр subView будет все еще ((20,20),(100,100)), но смещен на (20,20), потому что его система координат суперпредставления была смещена (20,20).
Надеюсь, это кому-нибудь поможет.
Позвольте мне добавить свои 5 центов.
Рамка используется родительским представлением для размещения его внутри родительского представления.
Границы используются самим представлением для размещения своего собственного содержимого (как при прокрутке). См. Также clipsToBounds. Границы также можно использовать для увеличения / уменьшения содержимого представления.
Аналогия:
Кадр ~ Экран ТВ
Границы ~ Камера (масштабирование, перемещение, поворот)
Все ответы выше верны, и я так понимаю:
Чтобы различать фрейм и границы, разработчик КОНЦЕПЦИИ должен прочитать:
- относительно супервизора (одного родительского представления) он содержится в = FRAME
- относительно его собственной системы координат, определяет его расположение подпредставления = BOUNDS
"bounds" сбивает с толку, потому что создается впечатление, что координаты - это позиция представления, для которого он установлен. Но они взаимосвязаны и регулируются в соответствии с константами фрейма.
Кадрируйте его относительно своего SuperView, тогда как Bounds относительно его NSView.
Пример:X=40,Y=60. Также содержит 3 вида. Эта диаграмма показывает вам ясную идею.
Приведенные выше ответы очень хорошо объяснили разницу между границами и рамками.
Границы: Размер и расположение вида согласно его собственной системе координат.
Кадр: размер представления и расположение относительно его SuperView.
Тогда возникает путаница, что в случае границ X,Y всегда будет "0". Что не соответствует действительности. Это можно понять в UIScrollView и UICollectionView.
Когда границы х, у не 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 имеет "х" на 320. Посмотрите на кадр imageView1.- Страница 2: Прокрутите страницу и перейдите на страницу 2 Границы: (x:640,y:0, ширина: 320, высота: 200) Снова взгляните на кадр imageView2
То же самое в случае UICollectionView. Самый простой способ взглянуть на collectionView - это прокрутить его и распечатать / записать его границы, и вы получите идею.
Рамка против границы
- Если вы создаете представление в координатах X:0, Y:0, width:400, height:400, его рамка и границы будут такими же.
- Если вы переместите это представление в X:400, его рамка отразит это изменение, а его границы - нет. Помните, что границы относятся к собственному пространству представления, и внутри представления ничего не изменилось.
- Если вы трансформируете представление, например, вращаете его или увеличиваете масштаб, рамка изменится, чтобы отразить это, но границы все равно не изменится - что касается внутреннего представления, оно не изменилось.
- Если вы измените границы, это изменит содержимое внутри фрейма, потому что начало прямоугольника границ начинается в другой части представления.
Вот отличное видео об этом (от Шона Аллена):
Swift — Границы против фрейма — вопрос интервью iOS
Фрейм — это позиция относительно координат супервизора:
а Bounds — это положение относительно его собственной системы координат.
И... становится сложнее, если вид повернут. (смотреть видео!)
Фрейм — это наименьший прямоугольник, содержащий представление.
Bounds — это в точности прямоугольник представления.
Frame = расположение и размер представления с использованием системы координат родительского представления
bounds = расположение и размер представления с использованием его собственной системы координат
Вид отслеживает свой размер и расположение с помощью двух прямоугольников: прямоугольника рамки и прямоугольника границ. Прямоугольник кадра определяет расположение и размер представления в супервизоре, используя систему координат супервизора. Прямоугольник границ определяет внутреннюю систему координат, которая используется при рисовании содержимого представления, включая начало координат и масштабирование. На рис. 2-1 показано соотношение между прямоугольником рамки слева и прямоугольником границ справа ".
Короче говоря, фрейм - это идея представления супервизором, а границы - это собственная идея представления. Наличие нескольких систем координат, по одной для каждого вида, является частью иерархии представлений.