cocoa: Qual a diferença entre o quadro e os limites?

UIView e suas subclasss têm as propriedades frame e bounds . Qual é a diferença?

Os limites de um UIView são o retângulo , expresso como um local (x, y) e tamanho (largura, altura) relativo ao seu próprio sistema de coordenadas (0,0).

O quadro de um UIView é o retângulo , expresso como um local (x, y) e tamanho (largura, altura) relativo à super visão em que está contido.

Portanto, imagine uma exibição que tenha um tamanho de 100 x 100 (largura x altura) posicionada a 25,25 (x, y) de sua superview. O código a seguir imprime os limites e quadro desta visão:

 // 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); } 

E a saída deste código é:

 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 

Assim, podemos ver que, em ambos os casos, a largura e a altura da vista são as mesmas, independentemente de estarmos olhando para os limites ou para o quadro. O que é diferente é o posicionamento x, y da vista. No caso dos limites, as coordenadas x e y estão em 0,0, pois essas coordenadas são relativas à visão em si. No entanto, as coordenadas do quadro x e y são relativas à posição da visão dentro da visão pai (que anteriormente dissemos que estava em 25,25).

Há também uma ótima apresentação que cobre o UIViews. Veja os slides 1-20, que não apenas explicam a diferença entre frameworks e limites, mas também mostram exemplos visuais.

Resposta curta

frame = localização e tamanho de uma vista usando o sistema de coordenadas da vista pai

  • Importante para: colocar a exibição no pai

limites = a localização e o tamanho de uma vista usando seu próprio sistema de coordenadas

  • Importante para: colocar o conteúdo ou subvisualizações da visão dentro de si

Resposta Detalhada

Para me ajudar a lembrar o quadro , penso em um quadro de imagem em uma parede . O quadro da imagem é como a borda de uma vista. Eu posso pendurar a imagem em qualquer lugar que eu quiser na parede. Da mesma forma, posso colocar uma visão em qualquer lugar que eu queira dentro de uma visão pai (também chamada de superview). A vista dos pais é como a parede. A origem do sistema de coordenadas no iOS é o canto superior esquerdo. Podemos colocar nossa visão na origem da super visão definindo as coordenadas xy do quadro de visão para (0, 0), que é como pendurar nossa imagem no canto superior esquerdo da parede. Para movê-lo para a direita, aumente x, para abaixá-lo, aumente y.

Para me ajudar a lembrar os limites , penso em uma quadra de basquete onde às vezes o basquete é derrubado . Você está driblando a bola por toda a quadra de basquete, mas você não se importa com a própria quadra. Pode ser em um ginásio, ou fora de uma escola, ou na frente de sua casa. Não importa. Você só quer jogar basquete. Da mesma forma, o sistema de coordenadas para os limites de uma visão só se preocupa com a visão em si. Ele não sabe nada sobre onde a exibição está localizada na exibição principal. A origem dos limites (ponto (0, 0) por padrão) é o canto superior esquerdo da exibição. Quaisquer subvisões que essa visão tenha são apresentadas em relação a este ponto. É como levar a bola de basquete para o canto esquerdo da quadra.

Agora a confusão surge quando você tenta comparar frameworks e limites. Na verdade, não é tão ruim quanto parece a princípio, no entanto. Vamos usar algumas fotos para nos ajudar a entender.

Frame vs Bounds

Na primeira imagem à esquerda, temos uma visão que está localizada no canto superior esquerdo de sua visão principal. O retângulo amarelo representa o quadro da vista. À direita, vemos a visualização novamente, mas desta vez a exibição principal não é mostrada. Isso porque os limites não sabem sobre a visão pai. O retângulo verde representa os limites da vista. O ponto vermelho nas duas imagens representa a origem do quadro ou limites.

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

insira a descrição da imagem aqui

Então o quadro e os limites eram exatamente os mesmos nessa foto. Vamos ver um exemplo onde eles são diferentes.

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

insira a descrição da imagem aqui

Então você pode ver que mudar as coordenadas xy do quadro o move na vista pai. Mas o conteúdo da visão em si ainda parece exatamente o mesmo. Os limites não têm ideia de que algo é diferente.

Até agora, a largura e a altura do quadro e dos limites foram exatamente os mesmos. Isso nem sempre é verdade, no entanto. Veja o que acontece se girarmos a vista 20 graus no sentido horário. (A rotação é feita usando transformações. Consulte a documentação e esses exemplos de visualização e camada para obter mais informações.)

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

insira a descrição da imagem aqui

Você pode ver que os limites ainda são os mesmos. Eles ainda não sabem que alguma coisa aconteceu! Os valores do quadro foram todos alterados, no entanto.

Agora é um pouco mais fácil ver a diferença entre frame e bounds, não é? O artigo Você provavelmente não compreende frameworks e limites define um quadro de

… a menor checkbox delimitadora desse ponto de vista em relação ao sistema de coordenadas dos pais, incluindo quaisquer transformações aplicadas a esse ponto de vista.

É importante observar que, se você transformar uma exibição, o quadro ficará indefinido. Então, na verdade, a moldura amarela que eu desenhei ao redor dos limites verdes girados na imagem acima nunca realmente existe. Isso significa que se você girar, dimensionar ou fazer alguma outra transformação, não deverá mais usar os valores de quadro. Você ainda pode usar os valores de limite, no entanto. Os documentos da Apple avisam:

Importante: Se a propriedade de transform uma visualização não contiver a transformação de identidade, o quadro dessa visualização será indefinido, assim como os resultados de seus comportamentos de redimensionamento automático.

Um pouco infeliz sobre o autoresizing …. Há algo que você pode fazer, no entanto.

O documento da Apple diz:

Ao modificar a propriedade de transform de sua visualização, todas as transformações são executadas em relação ao ponto central da exibição.

Portanto, se você precisar mover uma visualização no pai depois que uma transformação tiver sido feita, poderá fazê-lo alterando as coordenadas do view.center . Como o frame , o center usa o sistema de coordenadas da vista pai.

Ok, vamos nos livrar da rotação e focar nos limites. Até agora, a origem dos limites sempre permaneceu em (0, 0). Não precisa, no entanto. E se a nossa visualização tiver uma subvisão grande que seja grande demais para ser exibida de uma só vez? Nós vamos fazer um UIImageView com uma imagem grande. Aqui está novamente a nossa segunda foto, mas desta vez podemos ver como seria todo o conteúdo da sub-visão da nossa visão.

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

insira a descrição da imagem aqui

Apenas o canto superior esquerdo da imagem pode caber dentro dos limites da vista. Agora veja o que acontece se mudarmos as coordenadas de origem dos limites.

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

insira a descrição da imagem aqui

O quadro não foi movido na superview, mas o conteúdo dentro do quadro foi alterado porque a origem do retângulo de limites começa em uma parte diferente da vista. Essa é a ideia por trás de um UIScrollView e suas subclasss (por exemplo, um UITableView ). Veja Understanding UIScrollView para mais explicações.

Quando usar o quadro e quando usar limites

Como o frame relaciona a localização de uma vista em sua vista pai, você a usa quando faz alterações externas , como alterar sua largura ou encontrar a distância entre a vista e o topo de sua vista pai.

Use os bounds quando estiver fazendo alterações internas , como desenhar coisas ou organizar subvisualizações na exibição. Use também os limites para obter o tamanho da visualização, se tiver feito alguma transformação nela.

Artigos para pesquisa adicional:

Docs da apple

  • Visualizar geometry
  • Views
  • Arquitetura de visualização e janela

Perguntas relacionadas StackOverflow

  • Quadro UIView, limites e centro
  • Quadro de UIView, limites, centro, origem, quando usar o que?
  • Quadro “incorreto” / tamanho da janela após re-orientação no iPhone

Outros resources

  • Você provavelmente não entende frameworks e limites
  • Fundamentos do iOS: frameworks, limites e CGGeometry
  • Palestra CS193p 5 – Views, Desenho, Animação

Pratique-se

Além de ler os artigos acima, ajuda muito fazer um aplicativo de teste. Você pode querer tentar fazer algo semelhante. (Eu tive a idéia deste curso de vídeo, mas infelizmente não é de graça).

insira a descrição da imagem aqui

Aqui está o código para sua referência:

 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))" } } 

tente executar o código abaixo

 - (void)viewDidLoad { [super viewDidLoad]; UIWindow *w = [[UIApplication sharedApplication] keyWindow]; UIView *v = [w.subviews objectAtIndex:0]; NSLog(@"%@", NSStringFromCGRect(v.frame)); NSLog(@"%@", NSStringFromCGRect(v.bounds)); } 

a saída deste código é:

a orientação do dispositivo é Retrato

 {{0, 0}, {768, 1024}} {{0, 0}, {768, 1024}} 

orientação de dispositivo de caso é paisagem

 {{0, 0}, {768, 1024}} {{0, 0}, {1024, 768}} 

obviamente, você pode ver a diferença entre frame e bounds

O quadro é o retângulo que define o UIView em relação à sua super visão .

Os limites rect são o intervalo de valores que definem o sistema de coordenadas do NSView.

ou seja, qualquer coisa neste retângulo será realmente exibido no UIView.

frame é a origem (canto superior esquerdo) e tamanho da visão no sistema de coordenadas da sua super visão, isto significa que você traduz a visão em sua super visão mudando a origem do quadro, os limites por outro lado é o tamanho e origem em sua próprio sistema de coordenadas, então, por padrão, a origem dos limites é (0,0).

na maioria das vezes, o quadro e os limites são congruentes, mas se você tiver uma visualização do quadro ((140,65), (200,250)) e limites ((0,0), (200,250)), por exemplo, e a exibição foi inclinada de modo que fique no canto inferior direito, os limites ainda serão ((0,0), (200,250)), mas o quadro não é.

insira a descrição da imagem aqui

o quadro será o menor retângulo que encapsula / envolve a vista, de modo que o quadro (como na foto) será ((140,65), (320,320)).

outra diferença é por exemplo se você tem um superView cujos limites são ((0,0), (200,200)) e este superView tem um subView cujo frame é ((20,20), (100,100)) e você alterou os limites do superView para ((20,20), (200,200)), então o quadro subView será ainda ((20,20), (100,100)) mas compensado por (20,20) porque o sistema de coordenadas da super visão foi compensado por (20, 20).

Espero que isso ajude alguém.

Enquadre o seu relativo ao seu SuperView enquanto Bounds em relação ao seu NSView.

Exemplo: X = 40, Y = 60. Também contém 3 Visualizações. Este Diagrama mostra uma ideia clara.

QUADRO, ARMAÇÃO

SALTOS

As respostas acima explicaram muito bem a diferença entre limites e frameworks.

Limites: Um tamanho de vistas e localização como por seu próprio sistema de coordenadas.

Quadro: Tamanho e Localização relativos ao seu SuperView.

Então há confusão que no caso de Bounds o X, Y sempre será “0”. Isso não é verdade. Isso pode ser entendido no UIScrollView e no UICollectionView também.

Quando os limites x, y não são 0.
Vamos supor que temos um UIScrollView. Nós implementamos paginação. O UIScrollView tem 3 páginas e a largura do seu ContentSize é três vezes a largura da canvas (Assume que o ScreenWidth é 320). A altura é constante (suponha 200).

 scrollView.contentSize = CGSize(x:320*3, y : 200) 

Adicione três UIImageViews como subViews e observe atentamente o valor x do frame

  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) 
  1. Página 0: quando o ScrollView está na página 0, os limites serão (x: 0, y: 0, largura: 320, altura: 200)

  2. Página 1: Role e vá para a Página 1.
    Agora os limites serão (x: 320, y: 0, largura: 320, altura: 200) Lembre-se que dissemos em relação ao seu próprio sistema de coordenadas. Então agora a “parte visível” do nosso ScrollView tem seu “x” em 320. Olhe para o quadro de imageView1.

  3. Página 2: Role e vá para Página 2 Limites: (x: 640, y: 0, largura: 320, altura: 200) Novamente, dê uma olhada no quadro de imageView2

O mesmo é o caso do UICollectionView. A maneira mais fácil de olhar para collectionView é rolar e imprimir / registrar seus limites e você terá a idéia.