UIView
и все его подклассы имеют свойства frame
и bounds
. Какая разница?
UIView
и все его подклассы имеют свойства frame
и bounds
. Какая разница?
Ответы:
В пределах А. Н. UIView является прямоугольником , выраженным как местоположение (х, у) и размер (ширина, высота) по отношению к своей собственной системе координат (0,0).
Кадр из UIView представляет собой прямоугольник , выраженный как местоположение (х, у) и размер (ширина, высота) по отношению к надтаблице он содержится внутри.
Итак, представьте себе представление размером 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
В суперпредставлении рамка не сместилась, но содержимое внутри рамки изменилось, поскольку начало прямоугольника границ начинается в другой части представления. Это вся идея, стоящая за классом 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))"
}
}
bounds
против frame
из вида.
попробуйте запустить код ниже
- (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}}
очевидно, вы можете увидеть разницу между рамкой и границами
рамка - это начало координат (верхний левый угол) и размер представления в системе координат его суперпредставления, это означает, что вы переводите представление в его суперпредставление, изменяя начало координат рамки, с другой стороны , границы это размер и начало координат собственная система координат, поэтому по умолчанию начало координат равно (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).
Надеюсь, это кому-нибудь поможет.
subView
фрейм относительно этого superView
. изменение superView
границ ни к чему не приведет к тому, что subView
происхождение совпадет с этим superView
.
Кадрируйте его относительно своего SuperView, тогда как Bounds относительно его NSView.
Пример: X = 40, Y = 60. Также содержит 3 вида. Эта диаграмма показывает вам ясную идею.
Позвольте мне добавить мои 5 центов.
Frame используется родительским представлением представления, чтобы поместить его в родительское представление.
Границы используются самим представлением для размещения собственного содержимого (как при прокрутке во время прокрутки). Смотрите также clipsToBounds . Границы также могут быть использованы для увеличения / уменьшения содержимого представления.
Аналогия:
Рамка ~ Экран телевизора
Границы ~ Камера (масштабирование, перемещение, поворот)
Ответы выше очень хорошо объяснили разницу между границами и фреймами.
Границы: Размер и расположение вида согласно его собственной системе координат.
Кадр: размер представления и расположение относительно его SuperView.
Тогда возникает путаница, что в случае границ 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.
То же самое для случая UICollectionView. Самый простой способ взглянуть на collectionView - это прокрутить его и распечатать / записать его границы, и вы получите идею.
Все ответы выше верны, и это мое мнение об этом:
Чтобы различать рамку и границы, КОНЦЕПЦИИ разработчик должен прочитать:
- относительно суперпредставления (одно родительское представление) оно содержится в = FRAME
- относительно своей собственной системы координат, определяет ее местоположение подвид = BOUNDS
«границы» сбивают с толку, потому что создается впечатление, что координаты - это позиция вида, для которого он установлен. Но они находятся в отношениях и корректируются в соответствии с константами кадра.
Рамка против границы
frame = расположение и размер представления, используя систему координат родительского представления
bounds = расположение и размер представления с использованием его собственной системы координат
Представление отслеживает свой размер и местоположение, используя два прямоугольника: прямоугольник рамки и прямоугольник границ. Прямоугольник рамки определяет местоположение и размер представления в суперпредставлении, используя систему координат суперпредставления. Прямоугольник границ определяет внутреннюю систему координат, которая используется при рисовании содержимого вида, включая начало координат и масштабирование. На рисунке 2-1 показано соотношение между прямоугольником рамки слева и прямоугольником границ справа ».
Короче говоря, фрейм - это представление супервьюма о представлении, а границы - это собственная идея представления. Наличие нескольких систем координат, по одной для каждого вида, является частью иерархии видов.