Cacau: Qual é a diferença entre a moldura e os limites?

587

UIViewe todas as suas subclasses têm as propriedades framee bounds. Qual é a diferença?

mk12
fonte
1
Entendendo o quadro macoscope.com/blog/understanding-frame
onmyway133
para mim o mais explicação concisa está aqui: videos.raywenderlich.com/courses/99-scroll-view-school/lessons/...
Vlad
1
Não há nada de conciso em um vídeo das 2:30. Consigo ler uma frase ou duas muito mais rápido que 2:30.
dsjoerg
Para mim, é muito mais claro se eu pensar desta forma: Quadro = Bounds & Posição
Serg

Respostas:

902

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.

shek
fonte
37
Portanto, o x, y para limites sempre será 0,0, já que é a localização do objeto em ... em si. Ou você poderia dar um cenário onde não seria?
Mk12 31/07/09
5
Tanto quanto eu sei, a origem dos limites sempre será 0,0
shek
77
Na verdade, o bounds.origin pode ser algo além de 0,0. Use setBoundsOrigin: para mover / traduzir a origem. Consulte "Exibir geometria" no "Guia de programação de cacau" para obter mais informações.
31509 Meltemi
127
UIScrollView é um exemplo em que os limites podem mudar para mostrar diferentes regiões de conteúdo em uma exibição.
Brad Larson
92
Uma pequena dica - usar o NSStringFromCGRect pode economizar algum tempo para registrar rects.
berílio
647

Resposta curta

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

  • Importante para: colocar a vista no pai

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

  • Importante para: colocar o conteúdo ou as subvisões da visualização em si

Resposta detalhada

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.

Quadro vs limites

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

insira a descrição da imagem aqui

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

insira a descrição da imagem aqui

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

insira a descrição da imagem aqui

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 transformpropriedade 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 transformpropriedade 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.centercoordenadas. Como frame, centerusa 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 UIImageViewcom 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

insira a descrição da imagem aqui

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

insira a descrição da imagem aqui

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 UIScrollViewe suas subclasses (por exemplo, a UITableView). Consulte Noções básicas sobre o UIScrollView para obter mais explicações.

Quando usar quadro e quando usar limites

Como framerelaciona 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 boundsquando 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.

Artigos para pesquisas adicionais:

Documentos da Apple

Perguntas relacionadas ao StackOverflow

Outros recursos

Pratique-se

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.)

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

    }

}
Suragch
fonte
Parece que a largura e a altura do limite são redundantes e uma propriedade "origin" seria suficiente (como no Flash).
11117 Ian Warburton
use limites para "como desenhar coisas ou organizar subvisões dentro da vista". quer dizer, se eu tenho um viewController que possui dois botões, devo colocá-los usando os 'limites' da viewController ?! Eu sempre usei quadro da vista ...
Mel
@ Mel, estou um pouco enferrujado com minhas habilidades em iOS desde que trabalho no Android há um ano. Acredito que a visualização raiz de um View Controller preenche a tela, portanto seu quadro e limites devem ser os mesmos.
Suragch 17/11/19
5
Criado github.com/maniramezan/FrameVsBounds.git compromissadas com base no exemplo aplicativo dado aqui para ajudar a melhor na compreensão boundsvs framede um ponto de vista.
manman
1
@IanWarburton Quando a vista é girada, a largura e a altura do limite não coincidem com a do quadro e, portanto, não são redundantes.
Edward Brey
43

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

Tristan
fonte
2
isso não é mais uma verdade (iOS 8) #
Julian Król
13

O quadro é o retângulo que define a UIView com relação à sua superview .

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

ou seja, qualquer coisa nesse retângulo será exibida no UIView.

jorik
fonte
10

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.

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 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.

m.eldehairy
fonte
em relação ao último parágrafo: o subView quadro é relativo a ele superView. alterar os superViewlimites para qualquer coisa não fará com que a subVieworigem coincida com ela superView.
staticVoidMan
5

Enquadre seu relativo ao seu SuperView, enquanto o limite relativo ao seu NSView.

Exemplo: X = 40, Y = 60.Também contém 3 vistas. Este diagrama mostra uma ideia clara.

QUADRO, ARMAÇÃO

BOUNDS

RAGHUNATH
fonte
4

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)

Serg
fonte
2

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)
  1. Página 0: Quando o ScrollView está em 0 Página, 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 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.

  3. Página 2: Role e vá para Página 2 Limites: (x: 640, y: 0, largura: 320, altura: 200) Mais uma vez, observe o quadro do imageView2

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.

Muhammad Nayab
fonte
2

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:

  1. relativo à super visão (uma visão pai) está contida em = FRAME
  2. 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.

tela do iPhone

Marina
fonte
1

Mais uma ilustração para mostrar a diferença entre o quadro e os limites. Neste exemplo:

  • View B é uma subview de View A
  • View B foi movido para x:60, y: 20
  • View B foi girado 45 degrees

insira a descrição da imagem aqui

yoAlex5
fonte
0

Frame vs bound

  • Se você criar uma vista em X: 0, Y: 0, largura: 400, altura: 400, seu quadro e limites serão os mesmos.
  • Se você mover essa exibição para X: 400, seu quadro refletirá essa alteração, mas seus limites não. Lembre-se de que os limites são relativos ao espaço da vista e, internamente, nada mudou.
  • Se você transformar a vista, por exemplo, girando-a ou ampliando-a, o quadro mudará para refletir isso, mas os limites ainda não serão - no que diz respeito à vista internamente, ela não mudou.
  • Se você alterar os limites, o conteúdo dentro do quadro será alterado, porque a origem do retângulo dos limites começa em uma parte diferente da visualização.
Anit Kumar
fonte
-1

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.

Xab Ion
fonte
Onde está a Figura 2-1?
Alexander Mayatsky