UIView
e le sue sottoclassi hanno tutte le proprietà frame
e bounds
. Qual è la differenza?
UIView
e le sue sottoclassi hanno tutte le proprietà frame
e bounds
. Qual è la differenza?
Risposte:
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.
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
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.
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
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
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
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
transform
proprietà 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ò.
Quando si modifica la
transform
proprietà 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.center
coordinate. Come frame
, center
usa 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 UIImageView
con 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
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
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 UIScrollView
ed è sottoclassi (ad esempio, a UITableView
). Vedere Informazioni su UIScrollView per ulteriori spiegazioni.
Poiché mette in frame
relazione 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 bounds
quando 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.
Documenti Apple
Domande StackOverflow correlate
Altre risorse
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.)
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))"
}
}
bounds
vs frame
di una vista.
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
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 è.
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.
subView
frame è relativo ad esso superView
. cambiare i superView
limiti in qualcosa non farà subView
coincidere l' origine con essa superView
.
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)
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)
Pagina 0: quando ScrollView è nella pagina 0, i limiti saranno (x: 0, y: 0, larghezza: 320, altezza: 200)
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.
Lo stesso vale per UICollectionView. Il modo più semplice per guardare collectionView è scorrere e stampare / registrare i suoi limiti e otterrai l'idea.
Tutte le risposte sopra sono corrette e questa è la mia opinione su questo:
Per distinguere tra frame e limiti, lo sviluppatore CONCEPTS dovrebbe leggere:
- rispetto alla superview (una vista principale) è contenuta in = FRAME
- 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.
Frame vs bound
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.