Cacao: qual è la differenza tra cornice e limiti?


587

UIViewe le sue sottoclassi hanno tutte le proprietà framee bounds. Qual è la differenza?



per me la spiegazione più concisa è qui: videos.raywenderlich.com/courses/99-scroll-view-school/lessons/…
Vlad

1
Non c'è niente di conciso su un video delle 2:30. Riesco a leggere una frase o due molto più velocemente delle 2:30.
dsjoerg

Per me è molto più chiaro se la penso così: Frame = Bounds & Position
Serg

Risposte:


902

I limiti di una vista UIV sono il rettangolo , espresso come posizione (x, y) e dimensione (larghezza, altezza) rispetto al proprio sistema di coordinate (0,0).

Il frame di una UIView è il rettangolo , espresso come posizione (x, y) e dimensioni (larghezza, altezza) relative alla superview in cui è contenuto.

Quindi, immagina una vista che abbia una dimensione di 100x100 (larghezza x altezza) posizionata a 25,25 (x, y) della sua superview. Il codice seguente stampa i limiti e il frame di questa vista:

// 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 l'output di questo codice è:

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

Quindi, possiamo vedere che in entrambi i casi, la larghezza e l'altezza della vista sono le stesse indipendentemente dal fatto che stiamo osservando i limiti o la cornice. Ciò che è diverso è il posizionamento x, y della vista. Nel caso dei limiti, le coordinate xey sono a 0,0 in quanto tali coordinate sono relative alla vista stessa. Tuttavia, le coordinate del riquadro xey sono relative alla posizione della vista all'interno della vista padre (che in precedenza dicevamo era a 25,25).

C'è anche un'ottima presentazione che copre UIVview. Vedi le diapositive 1-20 che non solo spiegano la differenza tra frame e limiti, ma mostrano anche esempi visivi.


37
Quindi x, y per i limiti non sarà sempre 0,0 poiché è la posizione dell'oggetto in ... stesso. O potresti dare uno scenario in cui non sarebbe?
mk12

5
Per quanto ne so, l'origine dei limiti sarà sempre 0,0
mostra il

77
In realtà, i limiti. L'origine può essere qualcosa di diverso da 0,0. Usa setBoundsOrigin: per spostare / tradurre l'origine. Vedere "Visualizza geometria" nella "Visualizza guida alla programmazione per Cocoa" per maggiori informazioni.
Meltemi,

127
UIScrollView è un esempio in cui i limiti possono spostarsi per mostrare diverse aree di contenuto all'interno di una vista.
Brad Larson

92
Un piccolo suggerimento: l'utilizzo di NSStringFromCGRect può far risparmiare un po 'di tempo per registrare i rect.
berillio,

647

Risposta breve

frame = posizione e dimensioni di una vista utilizzando il sistema di coordinate della vista principale

  • Importante per: posizionare la vista nel genitore

limiti = posizione e dimensioni di una vista utilizzando il proprio sistema di coordinate

  • Importante per: posizionare il contenuto o le visualizzazioni secondarie della vista all'interno di se stessa

Risposta dettagliata

Per aiutarmi a ricordare la cornice , penso a una cornice su un muro . La cornice è come il bordo di una vista. Posso appendere l'immagine dove voglio sul muro. Allo stesso modo, posso mettere una vista ovunque io voglia all'interno di una vista genitore (anche chiamata superview). La vista principale è come il muro. L'origine del sistema di coordinate in iOS è in alto a sinistra. Possiamo mettere la nostra vista all'origine della superview impostando le coordinate xy della cornice della vista su (0, 0), che è come appendere la nostra foto nell'angolo in alto a sinistra del muro. Per spostarlo a destra, aumentare x, per spostarlo verso il basso aumentare y.

Per aiutarmi a ricordare i limiti , penso a un campo da basket in cui a volte il basket viene eliminato . Stai dribblando la palla in tutto il campo da basket, ma non ti interessa davvero dove si trova il campo stesso. Potrebbe essere in palestra, o fuori al liceo o davanti a casa tua. Non importa Vuoi solo giocare a basket. Allo stesso modo, il sistema di coordinate per i limiti di una vista si preoccupa solo della vista stessa. Non sa nulla di dove si trova la vista nella vista padre. L'origine dei limiti (punto (0, 0) per impostazione predefinita) è l'angolo in alto a sinistra della vista. Eventuali sottoview che questa vista ha sono presentate in relazione a questo punto. È come portare la pallacanestro nell'angolo anteriore sinistro del campo.

Ora la confusione arriva quando provi a confrontare frame e limiti. In realtà non è poi così male come sembra all'inizio. Usiamo alcune immagini per aiutarci a capire.

Frame vs Bounds

Nella prima immagine a sinistra abbiamo una vista che si trova nella parte superiore sinistra della vista padre. Il rettangolo giallo rappresenta la cornice della vista. Sulla destra vediamo di nuovo la vista ma questa volta la vista padre non viene mostrata. Questo perché i limiti non conoscono la vista padre. Il rettangolo verde rappresenta i limiti della vista. Il punto rosso in entrambe le immagini rappresenta l' origine della cornice o dei limiti.

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

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

inserisci qui la descrizione dell'immagine

Quindi la cornice e i limiti erano esattamente gli stessi in quella foto. Vediamo un esempio in cui sono diversi.

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

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

inserisci qui la descrizione dell'immagine

Quindi puoi vedere che cambiando le coordinate xy del frame si sposta nella vista genitore. Ma il contenuto della vista stessa sembra esattamente lo stesso. I limiti non hanno idea che qualcosa sia diverso.

Fino ad ora la larghezza e l'altezza sia del telaio che dei limiti sono state esattamente le stesse. Questo non è sempre vero, però. Guarda cosa succede se ruotiamo la vista di 20 gradi in senso orario. (La rotazione viene eseguita utilizzando le trasformazioni. Consultare la documentazione e questi esempi di vista e layer per ulteriori informazioni.)

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

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

inserisci qui la descrizione dell'immagine

Puoi vedere che i limiti sono sempre gli stessi. Non sanno ancora che è successo niente! I valori dei frame sono comunque cambiati.

Ora è un po 'più facile vedere la differenza tra frame e limiti, no? L'articolo che probabilmente non capisci frame e limiti definisce un frame di visualizzazione come

... il più piccolo riquadro di delimitazione di quella vista rispetto al suo sistema di coordinate padre, comprese eventuali trasformazioni applicate a quella vista.

È importante notare che se si trasforma una vista, la cornice diventa indefinita. Quindi, in realtà, la cornice gialla che ho disegnato attorno ai limiti verdi ruotati nell'immagine sopra non esiste mai. Ciò significa che se ruoti, ridimensioni o esegui qualche altra trasformazione, non dovresti più utilizzare i valori dei frame. Tuttavia, puoi comunque utilizzare i valori dei limiti. I documenti Apple avvertono:

Importante: se la transformproprietà di una vista non contiene la trasformazione dell'identità, il frame di quella vista non è definito, così come i risultati dei suoi comportamenti di ridimensionamento automatico.

Piuttosto sfortunato per il ridimensionamento automatico .... C'è qualcosa che puoi fare, però.

I documenti Apple dichiarano:

Quando si modifica la transformproprietà della vista, tutte le trasformazioni vengono eseguite in relazione al punto centrale della vista.

Pertanto, se è necessario spostare una vista nel genitore dopo aver effettuato una trasformazione, è possibile farlo modificando le view.centercoordinate. Come frame, centerusa il sistema di coordinate della vista padre.

Ok, liberiamoci della nostra rotazione e concentriamoci sui limiti. Finora l'origine dei limiti è sempre rimasta su (0, 0). Non deve, però. Che cosa succede se la nostra vista ha una sottoview di grandi dimensioni che è troppo grande per essere visualizzata tutta in una volta? Lo faremo UIImageViewcon un'immagine grande. Ecco di nuovo la nostra seconda immagine dall'alto, ma questa volta possiamo vedere come dovrebbe apparire l'intero contenuto della sottoview della nostra vista.

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

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

inserisci qui la descrizione dell'immagine

Solo l'angolo in alto a sinistra dell'immagine può rientrare nei limiti della vista. Ora guarda cosa succede se cambiamo le coordinate di origine dei limiti.

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

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

inserisci qui la descrizione dell'immagine

La cornice non si è spostata nella superview ma il contenuto all'interno della cornice è cambiato perché l'origine del rettangolo dei limiti inizia in una parte diversa della vista. Questa è l'idea alla base di a UIScrollViewed è sottoclassi (ad esempio, a UITableView). Vedere Informazioni su UIScrollView per ulteriori spiegazioni.

Quando usare la cornice e quando usare i limiti

Poiché mette in framerelazione la posizione di una vista nella sua vista principale, la usi quando stai apportando modifiche esterne , come cambiarne la larghezza o trovare la distanza tra la vista e la parte superiore della vista principale.

Usa il boundsquando stai apportando cambiamenti interiori , come disegnare cose o organizzare viste secondarie all'interno della vista. Utilizzare anche i limiti per ottenere le dimensioni della vista se è stata eseguita una trasfomazione su di essa.

Articoli per ulteriori ricerche:

Documenti Apple

Domande StackOverflow correlate

Altre risorse

Pratica te stesso

Oltre a leggere gli articoli sopra, mi aiuta molto a creare un'app di prova. Potresti voler provare a fare qualcosa di simile. (Ho avuto l'idea da questo video corso ma sfortunatamente non è gratuito.)

inserisci qui la descrizione dell'immagine

Ecco il codice per il tuo riferimento:

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

    }

}

Sembra che la larghezza e l'altezza del rilegato siano ridondanti e una proprietà di "origine" sarebbe stata sufficiente (come è stato con Flash).
Ian Warburton,

usa i limiti per "come disegnare oggetti o organizzare viste secondarie all'interno della vista". vuoi dire se ho un viewController che ha due pulsanti dovrei posizionare i pulsanti usando i 'limiti' della vista del viewController ?! Ho sempre usato la cornice della vista ...
Miele

@ Ciao, sono un po 'arrugginito sulle mie abilità iOS da quando ho lavorato su Android nell'ultimo anno. Credo che la vista principale di un View Controller riempia lo schermo, quindi la sua cornice e i suoi limiti dovrebbero essere gli stessi.
Suragch,

5
Creato github.com/maniramezan/FrameVsBounds.git Repo basa sull'esempio applicazione qui dato per aiutare meglio a comprendere boundsvs framedi una vista.
manman,

1
@IanWarburton Quando la vista viene ruotata, la larghezza e l'altezza del bordo non corrispondono a quelle del riquadro, quindi non sono ridondanti.
Edward Brey

43

prova a eseguire il codice qui sotto

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
    UIView *v = [w.subviews objectAtIndex:0];

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}

l'output di questo codice è:

l'orientamento del dispositivo case è Verticale

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

l'orientamento del dispositivo è Orizzontale

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

ovviamente, puoi vedere la differenza tra frame e limiti


2
questo non è più vero (iOS 8)
Julian Król,


13

La cornice è il rettangolo che definisce l'UIView rispetto alla sua superview .

Il limite corretto è l'intervallo di valori che definiscono il sistema di coordinate di NSView.

cioè qualsiasi cosa in questo rettangolo verrà effettivamente visualizzata in UIView.


10

frame è l'origine (angolo in alto a sinistra) e la dimensione della vista nel sistema di coordinate della sua super vista, ciò significa che traduci la vista nella sua super vista cambiando l'origine della cornice, i limiti invece sono la dimensione e l'origine nella sua proprio sistema di coordinate, quindi per impostazione predefinita l'origine dei limiti è (0,0).

il più delle volte la cornice e i limiti sono congruenti, ma se si ha una vista della cornice ((140,65), (200,250)) e dei limiti ((0,0), (200,250)) per esempio e la vista è stata inclinata in modo che si trovi sul suo angolo in basso a destra, i limiti saranno comunque ((0,0), (200.250)), ma la cornice non lo è.

inserisci qui la descrizione dell'immagine

la cornice sarà il rettangolo più piccolo che incapsula / circonda la vista, quindi la cornice (come nella foto) sarà ((140,65), (320,320)).

un'altra differenza è ad esempio se si dispone di un superView il cui limite è ((0,0), (200.200)) e questo superView ha un subView il cui frame è ((20,20), (100.100)) e sono stati modificati i limiti del superView a ((20,20), (200.200)), il riquadro subView sarà comunque ((20,20), (100.100)) ma compensato da (20,20) perché il suo sistema di coordinate di superview è stato compensato da (20, 20).

spero che questo aiuti qualcuno.


per quanto riguarda l'ultimo paragrafo: subView frame è relativo ad esso superView. cambiare i superViewlimiti in qualcosa non farà subViewcoincidere l' origine con essa superView.
staticVoidMan,

5

Inquadra il relativo rispetto al suo SuperView, mentre i limiti relativi al suo NSView.

Esempio: X = 40, Y = 60. Inoltre contiene 3 visualizzazioni. Questo diagramma mostra un'idea chiara.

TELAIO

LIMITI


4

Vorrei aggiungere i miei 5 centesimi.

Il frame viene utilizzato dalla vista padre della vista per posizionarlo all'interno della vista padre.

Bounds viene utilizzato dalla vista stessa per posizionare il proprio contenuto (come fa una vista di scorrimento durante lo scorrimento). Vedi anche clipsToBounds . I limiti possono anche essere utilizzati per ingrandire / ridurre il contenuto della vista.

Analogia:
Frame ~ Schermo TV
Limiti ~ Fotocamera (zoom, spostamento, rotazione)


2

Le risposte sopra hanno ben spiegato la differenza tra Bounds e Frames.

Limiti: dimensioni e posizione di una vista secondo il proprio sistema di coordinate.

Frame: dimensioni e posizione della vista relative al suo SuperView.

Quindi c'è confusione che in caso di limiti la X, Y sarà sempre "0". Questo non è vero . Questo può essere compreso anche in UIScrollView e UICollectionView.

Quando i limiti 'x, y non sono 0.
Supponiamo di avere UIScrollView. Abbiamo implementato l'impaginazione. UIScrollView ha 3 pagine e la sua larghezza di ContentSize è tre volte la larghezza dello schermo (supponiamo che ScreenWidth sia 320). L'altezza è costante (ipotizza 200).

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

Aggiungi tre UIImageView come visualizzazioni secondarie e osserva da vicino il valore x del 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. Pagina 0: quando ScrollView è nella pagina 0, i limiti saranno (x: 0, y: 0, larghezza: 320, altezza: 200)

  2. Pagina 1: scorrere e passare alla pagina 1.
    Ora i limiti saranno (x: 320, y: 0, larghezza: 320, altezza: 200) Ricorda che abbiamo detto rispetto al suo sistema di coordinate. Quindi ora la "parte visibile" del nostro ScrollView ha la sua "x" a 320. Guarda il frame di imageView1.

  3. Pagina 2: Scorri e passa a Pagina 2 Limiti: (x: 640, y: 0, larghezza: 320, altezza: 200) Ancora una volta dai un'occhiata alla cornice di imageView2

Lo stesso vale per UICollectionView. Il modo più semplice per guardare collectionView è scorrere e stampare / registrare i suoi limiti e otterrai l'idea.


2

Tutte le risposte sopra sono corrette e questa è la mia opinione su questo:

Per distinguere tra frame e limiti, lo sviluppatore CONCEPTS dovrebbe leggere:

  1. rispetto alla superview (una vista principale) è contenuta in = FRAME
  2. rispetto al proprio sistema di coordinate, determina la posizione della sottoview = RIMBORSO

"limiti" è confuso perché dà l'impressione che le coordinate siano la posizione della vista per cui è impostata. Ma questi sono nelle relazioni e regolati in base alle costanti del frame.

schermo dell'iPhone


1

Un'altra illustrazione per mostrare una differenza tra frame e limiti. In questo esempio:

  • View B è una sottoview di View A
  • View B è stato spostato a x:60, y: 20
  • View B è stato ruotato 45 degrees

inserisci qui la descrizione dell'immagine


0

Frame vs bound

  • Se si crea una vista in X: 0, Y: 0, larghezza: 400, altezza: 400, la cornice e i limiti sono gli stessi.
  • Se sposti quella vista su X: 400, la sua cornice rifletterà quel cambiamento ma i suoi limiti no. Ricorda, i limiti sono relativi allo spazio della vista e internamente alla vista non è cambiato nulla.
  • Se si trasforma la vista, ad es. Ruotandola o ridimensionandola, la cornice cambierà per riflettere ciò, ma i limiti non lo faranno - per quanto riguarda la vista internamente, non è cambiata.
  • Se modifichi i limiti, cambierà il contenuto all'interno della cornice perché l'origine del rettangolo dei limiti inizia in una parte diversa della vista.

-1

frame = posizione e dimensioni di una vista utilizzando il sistema di coordinate della vista principale

limiti = posizione e dimensioni di una vista utilizzando il proprio sistema di coordinate

Una vista tiene traccia delle sue dimensioni e posizione utilizzando due rettangoli: un rettangolo di riquadro e un rettangolo di limiti. Il rettangolo della cornice definisce la posizione e le dimensioni della vista nella superview usando il sistema di coordinate della superview. Il rettangolo dei limiti definisce il sistema di coordinate interno utilizzato per disegnare il contenuto della vista, inclusi origine e ridimensionamento. La Figura 2-1 mostra la relazione tra il rettangolo della cornice, a sinistra, e il rettangolo dei limiti, a destra. ”

In breve, la cornice è l'idea di una vista di superview e i limiti sono l'idea della vista. Avere più sistemi di coordinate, uno per ogni vista, fa parte della gerarchia della vista.


Dov'è la Figura 2-1?
Alexander Mayatsky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.