Какао:В чем разница между рамкой и границами?
-
06-07-2019 - |
Вопрос
UIView
и его подклассы обладают свойствами frame
и bounds
.Какая разница?
Решение
границы UIView - это rectangle , выраженный в виде местоположения (x, y) и размера (ширина, высота) относительно его собственной системы координат (0,0).
рамка UIView - это rectangle , выраженный в виде местоположения (x, y) и размера (ширина, высота) относительно суперпредставления, в котором он содержится.
Итак, представьте представление размером 100х100 (ширина х высота), расположенное на 25,25 (х, у) своего суперпредставления. Следующий код распечатывает границы и фрейм этого представления:
// 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, которые не только объясняют разницу между рамками и границами, но также показывают наглядные примеры.
Другие советы
Короткий ответ
рамка = расположение и размер представления с помощью система координат родительского представления
- Важно для:размещение представления в родительском
границы = расположение и размер представления с помощью своя система координат
- Важно для:размещение содержимого или подпредставлений внутри себя
Подробный ответ
Чтобы помочь мне вспомнить рамка, Я думаю о рамка для фотографий на стене.Рамка изображения подобна границе обзора.Я могу повесить картину на стену где захочу.Точно так же я могу поместить представление в любое место внутри родительского представления (также называемого суперпредставлением).Родительский вид похож на стену.Начало системы координат в iOS находится вверху слева.Мы можем поместить наше представление в начало суперпредставления, установив координаты x-y рамки вида на (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
Итак, вы можете видеть, что изменение координат x-y кадра перемещает его в родительском представлении.Но содержимое самого представления по-прежнему выглядит точно так же.Границы понятия не имеют, что что-то изменилось.
До сих пор ширина и высота рамки и границ были абсолютно одинаковыми.Однако это не всегда так.Посмотрите, что произойдет, если мы повернём вид на 20 градусов по часовой стрелке.(Вращение осуществляется с помощью преобразований.Посмотрите документация и эти вид и примеры слоев Чтобы получить больше информации.)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
Вы можете видеть, что границы все те же.Они до сих пор не знают, что что-то произошло!Однако все значения кадров изменились.
Теперь стало немного легче увидеть разницу между рамкой и границами, не так ли?Статья Вы, вероятно, не понимаете рамок и границ определяет рамку просмотра как
... наименьшая ограничивающая рамка этого представления по отношению к его родителям системы координат, включая любые преобразования, примененные к этому виду.
Важно отметить, что если вы преобразуете представление, фрейм становится неопределенным.Так что на самом деле желтая рамка, которую я нарисовал вокруг повернутой зеленой границы на изображении выше, на самом деле никогда не существует.Это означает, что если вы вращаете, масштабируете или выполняете какое-либо другое преобразование, вам больше не следует использовать значения кадра.Однако вы все равно можете использовать значения границ.Документы Apple предупреждают:
Важный: Если вид
transform
не содержит преобразования Identity, фрейм этого представления не определен, как и свойство результаты его поведения при автоматическом изменении размера.
Довольно неудачно с автоматическим изменением размера....Однако кое-что вы можете сделать.
При изменении
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
Связанные вопросы по StackOverflow
- Рамка 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.
frame - это начало координат (верхний левый угол) и размер представления в системе координат его суперпредставления, это означает, что вы переводите представление в его суперпредставлении путем изменения источника кадра, bounds - это размер и начало координат в собственной системе координат, поэтому по умолчанию начало координат равно (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)), и вы изменили superView ограничивается ((20,20), (200,200)), тогда кадр subView будет все еще ((20,20), (100,100)), но смещен на (20,20), потому что его система координат суперпредставления была смещена (20,20). Р>
Надеюсь, это кому-нибудь поможет.
Кадрируйте его относительно своего SuperView, тогда как Bounds относительно его NSView.
Пример: X = 40, Y = 60. Также содержит 3 вида. На этой диаграмме показано четкое представление.
Ответы выше очень хорошо объяснили разницу между границами и фреймами. Р>
Границы: размер и расположение вида в соответствии с его собственной системой координат.
Рамка: размер и расположение вида относительно его SuperView.
Тогда возникает путаница, что в случае Bounds 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 показана взаимосвязь между прямоугольником рамки слева и прямоугольником границ справа. & # 8221;
Короче говоря, фрейм - это представление представления о суперпредставлении, а границы - это собственная идея представления. Наличие нескольких систем координат, по одной для каждого вида, является частью иерархии видов.