Короткий ответ
frame = расположение и размер представления с использованием системы координат родительского представления
- Важно для: размещения представления в родительском
bounds = местоположение и размер вида с использованием его собственной системы координат
- Важно для: размещения содержимого или подпредставлений представления внутри себя
Подробный ответ
Чтобы помочь мне вспомнить рамку , я думаю о рамку на стену . Рамка рисунка похожа на границу вида. Я могу повесить картину где угодно на стене. Таким же образом я могу поместить представление в любом месте внутри родительского представления (также называемого суперпредставлением). Родительский вид похож на стену. Начало системы координат в 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 градусов по часовой стрелке. (Поворот выполняется с использованием преобразований. Для получения дополнительной информации см. Документацию и эти view и примеры слоев .)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
Вы можете видеть, что границы все те же. Они до сих пор не знают, что случилось! Однако все значения кадра изменились.
Теперь немного легче увидеть разницу между рамкой и границами, не так ли? В статье Вы, вероятно, не понимаете рамки и границы определяет рамку вида как
... самая маленькая ограничивающая рамка этого представления по отношению к родителям
система координат, включая любые преобразования, примененные к этому представлению.
Важно отметить, что если вы преобразуете вид, то рамка становится неопределенной. На самом деле, желтая рамка, которую я нарисовал вокруг повернутых зеленых границ на изображении выше, на самом деле никогда не существует. Это означает, что если вы вращаете, масштабируете или делаете какое-то другое преобразование, вам больше не следует использовать значения кадра. Вы все еще можете использовать значения границ, хотя. Документы Apple предупреждают:
Важное замечание: Если свойство представления transform
не содержит преобразования идентификаторов, кадр этого представления не определен, как и
результаты его авторазмерного поведения.
Скорее неудачно с автоматическим изменением размера .... Но есть кое-что, что вы можете сделать.
Состояние документов Apple:
При изменении свойства 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 Docs
Вопросы, связанные с 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))"
}
}