UIView
e todas as suas subclasses têm as propriedades frame
e bounds
. Qual é a diferença?
UIView
e todas as suas subclasses têm as propriedades frame
e bounds
. Qual é a diferença?
Respostas:
Os limites de uma UIView são o retângulo , expresso como uma localização (x, y) e tamanho (largura, altura) em relação ao seu próprio sistema de coordenadas (0,0).
O quadro de uma UIView é o retângulo , expresso como um local (x, y) e tamanho (largura, altura) em relação à super visão em que está contida.
Então, imagine uma vista com um tamanho de 100x100 (largura x altura) posicionado em 25,25 (x, y) de sua super visão. O código a seguir imprime os limites e o quadro dessa exibiçã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 desse 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
Portanto, podemos ver que, em ambos os casos, a largura e a altura da vista são as mesmas, independentemente de estarmos olhando os limites ou 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 à própria vista. No entanto, as coordenadas x e y do quadro são relativas à posição da vista na vista principal (que anteriormente dissemos ser de 25,25).
Há também uma ótima apresentação que abrange UIViews. Veja os slides 1 a 20, que não apenas explicam a diferença entre quadros e limites, mas também mostram exemplos visuais.
frame = localização e tamanho de uma vista usando o sistema de coordenadas da vista pai
limites = localização e tamanho de uma vista usando seu próprio sistema de coordenadas
Para me ajudar a lembrar da moldura , penso em uma moldura na parede . A moldura da imagem é como a borda de uma vista. Eu posso pendurar a foto em qualquer lugar que eu quiser na parede. Da mesma maneira, posso colocar uma visualização em qualquer lugar que eu queira dentro da visualização principal (também chamada de superview). A visão dos pais é como a parede. A origem do sistema de coordenadas no iOS é a parte superior esquerda. Podemos colocar nossa visão na origem da superview, definindo as coordenadas xy do quadro de vista como (0, 0), o que é como pendurar nossa imagem no canto superior esquerdo da parede. Para movê-lo para a direita, aumente x, para movê-lo para baixo, aumente y.
Para me ajudar a lembrar limites , penso em uma quadra de basquete, onde às vezes o basquete é eliminado . Você está driblando a bola por toda a quadra de basquete, mas realmente não se importa onde fica a quadra. Pode ser em uma academia, 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 dos limites de uma vista se importa apenas com a vista em si. Ele não sabe nada sobre onde a exibição está localizada na exibição pai. A origem dos limites (ponto (0, 0) por padrão) é o canto superior esquerdo da visualização. Quaisquer subvisões que essa visualização possui são apresentadas em relação a este ponto. É como levar a bola para o canto esquerdo da frente da quadra.
Agora a confusão vem quando você tenta comparar o quadro e os limites. Na verdade, não é tão ruim quanto parece à primeira vista. Vamos usar algumas fotos para nos ajudar a entender.
Na primeira imagem à esquerda, temos uma visualização localizada no canto superior esquerdo da visualização pai. O retângulo amarelo representa o quadro da vista. À direita, vemos a visualização novamente, mas desta vez a visualização principal não é mostrada. Isso ocorre porque os limites não conhecem a visão dos pais. O retângulo verde representa os limites da vista. O ponto vermelho nas duas imagens representa a origem do quadro ou dos limites.
Frame
origin = (0, 0)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Portanto, o quadro e os limites eram exatamente os mesmos nessa imagem. Vejamos um exemplo em que 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
Assim, você pode ver que alterar as coordenadas xy do quadro o move na visualização principal. Mas o conteúdo da visualização em si ainda parece exatamente o mesmo. Os limites não fazem ideia de que algo é diferente.
Até agora, a largura e a altura do quadro e dos limites eram exatamente iguais. 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 exibiçã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
Você pode ver que os limites ainda são os mesmos. Eles ainda não sabem que nada aconteceu! Todos os valores do quadro foram alterados.
Agora é um pouco mais fácil ver a diferença entre quadro e limites, não é? O artigo Você provavelmente não entende quadros e limites define um quadro de vista como
... a menor caixa delimitadora dessa visualização em relação ao sistema de coordenadas dos pais, incluindo quaisquer transformações aplicadas a essa visualização.
É importante observar que, se você transformar uma vista, o quadro ficará indefinido. Na verdade, a moldura amarela que desenhei em torno dos limites verdes girados na imagem acima nunca existe. Isso significa que, se você girar, dimensionar ou fazer alguma outra transformação, não deverá mais usar os valores do quadro. Você ainda pode usar os valores dos limites, no entanto. Os documentos da Apple alertam:
Importante: Se a
transform
propriedade de uma exibição não contiver a transformação de identidade, o quadro dessa exibição será indefinido e os resultados de seus comportamentos de redimensionamento automático.
Bastante infeliz sobre o redimensionamento automático ... Porém, há algo que você pode fazer.
Os documentos da Apple afirmam:
Ao modificar a
transform
propriedade da sua visualização, todas as transformações são executadas em relação ao ponto central da visualização.
Portanto, se você precisar mover uma vista no pai após uma transformação, poderá fazê-lo alterando as view.center
coordenadas. Como frame
, center
usa o sistema de coordenadas da visualização pai.
Ok, vamos nos livrar da nossa rotação e focar nos limites. Até agora, a origem dos limites sempre permaneceu em (0, 0). Não precisa, no entanto. E se nossa visão tiver uma subvisualização grande demais para ser exibida de uma só vez? Nós vamos fazer isso UIImageView
com uma imagem grande. Aqui está nossa segunda foto de cima novamente, mas desta vez podemos ver como seria todo o conteúdo da subvisualização de nossa visualização.
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Somente o canto superior esquerdo da imagem pode caber dentro dos limites da vista. Agora veja o que acontece se alterarmos as coordenadas de origem dos limites.
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (280, 70)
width = 80
height = 130
O quadro não se moveu 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 visualização. Essa é toda a ideia por trás de a UIScrollView
e suas subclasses (por exemplo, a UITableView
). Consulte Noções básicas sobre o UIScrollView para obter mais explicações.
Como frame
relaciona a localização de uma vista em sua visualização principal, você a usa ao fazer alterações externas , como alterar sua largura ou encontrar a distância entre a visualização e a parte superior da visualização principal.
Use bounds
quando você estiver fazendo alterações internas , como desenhar objetos ou organizar subvisões dentro da vista. Use também os limites para obter o tamanho da visualização, se você fez alguma transfomação nela.
Documentos da Apple
Perguntas relacionadas ao StackOverflow
Outros recursos
Além de ler os artigos acima, me ajuda muito a criar um aplicativo de teste. Você pode tentar fazer algo semelhante. (Tive a ideia deste curso em vídeo, mas infelizmente não é gratuito.)
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))"
}
}
bounds
vs frame
de um ponto de vista.
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 desse código é:
caso a orientação do dispositivo seja Vertical
{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}
caso a orientação do dispositivo seja Horizontal
{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}
obviamente, você pode ver a diferença entre o quadro e os limites
quadro é a origem (canto superior esquerdo) e tamanho da vista no sistema de coordenadas da super visão, isso significa que você traduz a vista na super visão alterando a origem do quadro, os limites por outro lado, são o tamanho e a origem em sua vista. próprio sistema de coordenadas, portanto, por padrão, a origem dos limites é (0,0).
na maioria das vezes, o quadro e os limites são congruentes, mas se você tem uma visão do quadro ((140,65), (200,250)) e dos limites ((0,0), (200,250)), por exemplo, a vista é inclinada para que fique no canto inferior direito, os limites ainda serão ((0,0), (200,250)), mas o quadro não.
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 esse superView tem um subView cujo quadro é ((20,20), (100,100)) e você alterou os limites do superView para ((20,20), (200,200)), o quadro subView será parado ((20,20), (100,100)), mas compensado por (20,20) porque seu sistema de coordenadas de superview foi compensado por (20, 20)
Espero que isso ajude alguém.
subView
quadro é relativo a ele superView
. alterar os superView
limites para qualquer coisa não fará com que a subView
origem coincida com ela superView
.
Deixe-me adicionar meus 5 centavos.
O quadro é usado pela visualização pai da exibição para colocá-lo dentro da exibição pai.
Limites é usado pela própria exibição para colocar seu próprio conteúdo (como faz uma exibição de rolagem durante a rolagem). Veja também clipsToBounds . Os limites também podem ser usados para ampliar / reduzir o conteúdo da visualização.
Analogia:
Moldura ~
Limites da tela da TV ~ Câmera (zoom, mover, girar)
As respostas acima explicaram muito bem a diferença entre limites e quadros.
Limites: Uma visualização Tamanho e localização, de acordo com seu próprio sistema de coordenadas.
Quadro: um tamanho e local da vista em relação ao seu SuperView.
Depois, existe a confusão de que, no caso de limites, o X, Y sempre será "0". Isto não é verdade . Isso também pode ser entendido no UIScrollView e UICollectionView.
Quando os limites 'x, y não são 0.
Vamos assumir que temos um UIScrollView. Implementamos a paginação. O UIScrollView possui 3 páginas e a largura do seu ContentSize é três vezes a largura da tela (suponha que a largura da tela seja 320). A altura é constante (assuma 200).
scrollView.contentSize = CGSize(x:320*3, y : 200)
Adicione três UIImageViews como subViews e observe atentamente o valor x do quadro
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)
Página 0: Quando o ScrollView está em 0 Página, os limites serão (x: 0, y: 0, largura: 320, altura: 200)
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 do 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. Veja o quadro do imageView1.
Mesmo para o caso de UICollectionView. A maneira mais fácil de ver o collectionView é rolá-lo e imprimir / registrar seus limites e você terá a ideia.
Todas as respostas acima estão corretas e esta é minha opinião sobre o seguinte:
Para diferenciar entre o quadro e os limites, o desenvolvedor do CONCEPTS deve ler:
- relativo à super visão (uma visão pai) está contida em = FRAME
- em relação ao seu próprio sistema de coordenadas, determina sua localização da subvisão = BOUNDS
"limites" é confuso porque dá a impressão de que as coordenadas são a posição da vista para a qual está definida. Mas estas estão em relações e ajustadas de acordo com as constantes do quadro.
Frame vs bound
frame = localização e tamanho de uma vista usando o sistema de coordenadas da vista pai
limites = localização e tamanho de uma vista usando seu próprio sistema de coordenadas
Uma vista rastreia seu tamanho e localização usando dois retângulos: um retângulo de quadro e um retângulo de limites. O retângulo do quadro define a localização e o tamanho da vista na superview usando o sistema de coordenadas da superview. O retângulo de limites define o sistema de coordenadas internas que é usado ao desenhar o conteúdo da vista, incluindo a origem e a escala. A Figura 2-1 mostra a relação entre o retângulo do quadro, à esquerda, e o retângulo de limites, à direita. ”
Em resumo, o quadro é a ideia de uma visão da superview, e os limites é a própria idéia da visão. Ter vários sistemas de coordenadas, um para cada visualização, faz parte da hierarquia da visualização.