Cocoa: jaka jest różnica między kadrem a granicami?
UIView
wszystkie jego podklasy mają właściwości frame
i bounds
. Co za różnica?
11 answers
Granice UIView to prostokąt, wyrażony jako położenie (x,y) i rozmiar (szerokość,wysokość) względem własnego układu współrzędnych (0,0).
Ramka zUIView jest prostokątem , wyrażonym jako położenie (x,y) i rozmiar (szerokość,wysokość) względem superview, w którym jest zawarty.
Wyobraźmy sobie więc widok, który ma rozmiar 100x100 (szerokość x wysokość) ustawiony na 25,25 (X, y) swojego superwizora. Następujące kod wyświetla granice i ramkę tego widoku:
// 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);
}
A wyjście tego kodu to:
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
Widzimy więc, że w obu przypadkach szerokość i wysokość widoku są takie same niezależnie od tego, czy patrzymy na granice czy ramkę. Czym się różni pozycjonowanie X, y widoku. W przypadku granic współrzędne x i y są równe 0,0, ponieważ te współrzędne są względem samego widoku. Jednak współrzędne ramki x i Y są względem pozycja widoku w widoku nadrzędnym (o którym wcześniej mówiliśmy, że wynosi 25,25).
Istnieje również świetna prezentacja , która obejmuje widoki. Zobacz slajdy 1-20, które nie tylko wyjaśniają różnicę między ramkami i granicami, ale także pokazują przykłady wizualne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-28 19:36:47
Krótka Odpowiedź
Ramka = lokalizacja i rozmiar widoku przy użyciu układu współrzędnych widoku nadrzędnego
- Ważne dla: umieszczenie widoku w rodzicu
Bounds = położenie i rozmiar widoku za pomocą własnego układu współrzędnych
- Ważne dla: umieszczania zawartości lub podwidywań widoku w sobie
Szczegółowa Odpowiedź
Aby pomóc mi zapamiętać frame , I pomyśl o ramce do zdjęć na ścianie. Ramka obrazu jest jak granica widoku. Mogę powiesić obraz, gdzie tylko zechcę na ścianie. W ten sam sposób mogę umieścić widok w dowolnym miejscu wewnątrz widoku nadrzędnego (zwanego także superview). Widok rodzica jest jak ściana. Początek układu współrzędnych w systemie iOS to lewy górny róg. Możemy umieścić nasz widok na początku superview ustawiając współrzędne x-y ramki widoku na (0, 0), co jest jak powieszenie naszego obrazu na samej górze lewy róg ściany. Aby przesunąć w prawo, zwiększ x, aby przesunąć w dół zwiększ y.
Aby pomóc mi zapamiętać granice , myślę o boisku do koszykówkigdzie czasami koszykówka dostaje knocked out of bounds. Drybling piłkę na całym boisku do koszykówki, ale naprawdę nie obchodzi cię, gdzie sam sąd jest. Może to być na siłowni, na zewnątrz w szkole średniej lub przed Twoim domem. Nieważne. Chcesz po prostu grać w kosza. W tak samo, układ współrzędnych dla granic widoku dba tylko o sam widok. Nie wie nic o tym, gdzie znajduje się widok w widoku nadrzędnym. Początek obwiedni (punkt (0, 0) domyślnie) jest lewym górnym rogiem widoku. Wszelkie podglądy tego widoku są określone w odniesieniu do tego punktu. To jest jak biorąc koszykówkę do przedniego lewego rogu sądu.
Teraz pojawia się zamieszanie, gdy próbujesz porównać ramki i granice. Właściwie to nie jest tak źle jak wydaje się, że na początku. Użyjmy kilku zdjęć, aby pomóc nam zrozumieć.
Frame vs Bounds
Na pierwszym obrazku po lewej stronie mamy widok, który znajduje się w lewym górnym rogu widoku nadrzędnego. żółty prostokąt reprezentuje ramkę widoku. po prawej stronie widzimy widok ponownie, ale tym razem widok nadrzędny nie jest pokazany. To dlatego, że granice nie wiedzą o widoku rodzica. zielony prostokąt reprezentuje granice widoku. The red kropka na obu obrazach reprezentuje pochodzenie ramki lub obramowania.
Frame
origin = (0, 0)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Więc rama i granice były dokładnie takie same na tym zdjęciu. Spójrzmy na przykład, gdzie są różne.
Frame
origin = (40, 60) // That is, x=40 and y=60
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Więc widać, że zmiana współrzędnych x-y ramki przesuwa ją w widoku nadrzędnym. Ale treść samego widoku nadal wygląda dokładnie tak samo. Bounds nie mają pojęcia, że wszystko jest inne.
Do tej pory szerokość i wysokość zarówno ramki, jak i obrzeży były dokładnie takie same. Ale to nie zawsze prawda. Zobacz, co się stanie, jeśli obrócimy widok o 20 stopni zgodnie z ruchem wskazówek zegara. (Obrót odbywa się za pomocą przekształceń. Więcej informacji można znaleźć w dokumentacji oraz w poniższym widoku i przykładach warstw .)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
Widać, że granice są nadal takie same. Nadal nie wiedzą, że coś się stało! Ramka wartości się jednak zmieniły.
Teraz jest trochę łatwiej dostrzec różnicę między ramką a obramowaniem, prawda? W artykule prawdopodobnie nie rozumiesz ramek i granic definiuje ramkę widoku jako
...najmniejszą obwiednię tego widoku w odniesieniu do jego rodziców układ współrzędnych, w tym wszelkie przekształcenia zastosowane do tego widoku.
Należy pamiętać, że jeśli przekształcisz widok, ramka stanie się undefined. Tak naprawdę żółta ramka, którą narysowałem wokół obróconych zielonych granic na powyższym obrazku, nigdy nie istnieje. Oznacza to, że jeśli obracasz, skalujesz lub wykonujesz inną transformację, nie powinieneś już używać wartości ramki. Nadal można jednak używać wartości obwiedni. Apple docs ostrzega:
Ważne: jeśli właściwość widoku
transform
nie zawiera transformacji tożsamości, ramka tego widoku jest niezdefiniowana, a więc wyniki its autoresistowskie zachowania.
Raczej niefortunne z powodu autoresizacji.... Jest jednak coś, co możesz zrobić.
Podczas modyfikowania właściwości
transform
twojego widoku, wszystkie transformacje są wykonywane względem punktu centralnego widok.
Więc jeśli musisz przenieść widok w rodzicu po wykonaniu transformacji, możesz to zrobić, zmieniając współrzędne view.center
. Jak frame
, center
używa układu współrzędnych widoku nadrzędnego.
UIImageView
z dużym obrazem. Oto nasze drugie zdjęcie z góry, ale tym razem możemy zobaczyć, jak wyglądałaby cała zawartość subview naszego widoku na przykład.
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Tylko lewy górny róg obrazu może zmieścić się w granicach widoku. Zobacz, co się stanie, jeśli zmienimy współrzędne.
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (280, 70)
width = 80
height = 130
Ramka nie została przesunięta w widoku superview, ale zawartość wewnątrz ramki uległa zmianie, ponieważ początek prostokąta granic zaczyna się od innej części widoku. To jest cała idea UIScrollView
i to są podklasy(na przykład, a UITableView
). Zobacz też zrozumienie UIScrollView aby uzyskać więcej wyjaśnień.
Kiedy używać ramki i kiedy używać granic
Ponieważ frame
odnosi się do lokalizacji widoku nadrzędnego, używasz go podczas dokonywania zmian zewnętrznych, takich jak zmiana jego szerokości lub Znajdowanie odległości między widokiem a szczytem widoku nadrzędnego.
Użyj bounds
podczas dokonywania zmian wewnętrznych , takich jak rysowanie rzeczy lub porządkowanie podwidrzeń w widoku. Korzystaj również z ograniczeń aby uzyskać rozmiar widoku, jeśli zrobiłeś na nim transfomację.
Artykuły do dalszych badań:
Apple docs
Powiązane pytania dotyczące StackOverflow
- UIView frame, bounds and center
- UIView ' s frame, bounds, center, origin, when to use co?
- "nieprawidłowy rozmiar ramki / okna po ponownej orientacji w iPhonie
Inne zasoby
- prawdopodobnie nie rozumiesz ramek i granic
- iOS: Frames, Bounds, and CGGeometry
- CS193p Wykład 5-Widoki, Rysunek, Animacja
Ćwicz siebie
Oprócz czytania powyższych artykułów, bardzo pomaga mi zrobić test app. Możesz spróbować zrobić coś podobnego. (Pomysł zaczerpnąłem z tego kursu wideo, ale niestety nie jest on darmowy.)
Oto kod w celach informacyjnych:
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))"
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-10 05:12:34
Spróbuj uruchomić poniższy kod
- (void)viewDidLoad {
[super viewDidLoad];
UIWindow *w = [[UIApplication sharedApplication] keyWindow];
UIView *v = [w.subviews objectAtIndex:0];
NSLog(@"%@", NSStringFromCGRect(v.frame));
NSLog(@"%@", NSStringFromCGRect(v.bounds));
}
Wyjście tego kodu to:
Orientacja urządzenia jest portretowa
{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}
Orientacja urządzenia Case jest pozioma
{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}
Oczywiście widać różnicę między ramką a obramowaniem
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-04 01:44:21
Ramka jest prostokątem, który definiuje UIView z względem superview .
Granice rect to zakres wartości, które definiują układ współrzędnych NSView.
Czyli wszystko w tym prostokącie będzie faktycznie wyświetlane w UIView.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-07 18:21:19
Ramka jest początkiem (lewy górny róg) i rozmiarem widoku w układzie współrzędnych Super view , co oznacza, że można przetłumaczyć widok w super view zmieniając pochodzenie ramki, granice z drugiej strony to rozmiar i pochodzenie we własnym układzie współrzędnych, więc domyślnie początek granic wynosi (0,0).
Większość czasu ramka i granice są zgodne , ale jeśli masz widok ramki ((140,65),(200,250)) i granice ((0,0), (200,250)) na przykład i Widok został przechylony tak, że stoi w prawym dolnym rogu, wtedy granice nadal będą ((0,0),(200,250)) , ale rama nie jest .
Ramka będzie najmniejszym prostokątem, który otacza Widok,więc ramka (jak na zdjęciu) będzie ((140,65),(320,320)).
Inną różnicą jest na przykład, jeśli masz superView, którego granice są ((0,0), (200,200)) i ten superView ma subView, którego ramka jest ((20,20),(100,100)) i zmieniłeś Superwizjer do ((20,20),(200,200)) , następnie ramka subView będzie nadal ((20,20), (100,100)), ale przesunięta o (20,20), ponieważ jego układ współrzędnych superview został przesunięty o (20,20).
Mam nadzieję, że to komuś pomoże.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-06 19:13:14
Frame its relative to its SuperView, podczas gdy Bounds relative to its NSView.
Przykład: X = 40, Y=60.Zawiera również 3 widoki.Ten Diagram pokazuje jasny pomysł.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 01:02:13
Dodam moje 5 centów.
Ramka jest używana przez Widok nadrzędny, aby umieścić ją wewnątrz widoku nadrzędnego.
Bounds jest używany przez sam widok do umieszczania własnej zawartości(podobnie jak widok przewijania robi to podczas przewijania). Zobacz także clipsToBounds . Ograniczenia mogą być również używane do powiększania / pomniejszania zawartości widoku.
Analogia:
Ramka ~ ekran TV
Bounds ~ Camera (zoom, move, rotate)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-13 13:56:29
Wszystkie powyższe odpowiedzi są poprawne i to jest moje zdanie na ten temat:
Aby odróżnić pojęcia ramki od granic programista powinien przeczytać:
- w stosunku do superview (widok rodzica) jest zawarty w = FRAME
- względem własnego układu współrzędnych, określa jego położenie podrzędne = granice
"granice" jest mylące, ponieważ sprawia wrażenie, że współrzędne są pozycją widoku, dla którego jest ustawiona. Ale są one w relacjach i dostosowane do stałych ramek.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-17 19:37:55
Powyższe odpowiedzi bardzo dobrze wyjaśniły różnicę między obramowaniami a obramowaniami.
Granice: rozmiar i położenie widoku według własnego układu współrzędnych.
Frame : rozmiar i lokalizacja widoku względem SuperView.
Wtedy jest zamieszanie, że w przypadku granic X, Y zawsze będzie "0". to nieprawda. Można to zrozumieć również w UIScrollView i UICollectionView.
Gdy X, y nie są 0.
Załóżmy, że mamy UIScrollView. Wdrożyliśmy paginację. UIScrollView ma 3 strony, a jego szerokość ContentSize jest trzykrotnie większa niż szerokość ekranu (Załóżmy, że szerokość ekranu wynosi 320). Wysokość jest stała (Załóżmy 200).
scrollView.contentSize = CGSize(x:320*3, y : 200)
Dodaj trzy widoki UIImageView jako podview i miej oko na x wartość ramki
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)
Strona 0: Gdy ScrollView znajduje się na stronie 0, granice będą (x:0,y:0, width : 320, height: : 200)
Strona 1: Przewiń i przejdź do strony 1.
Teraz granice będą (x:320,y:0, width : 320, height : 200) Pamiętaj, że powiedzieliśmy w odniesieniu do własnego układu współrzędnych. Tak więc teraz "widoczna część" naszego przewijania ma " x " na poziomie 320. Spójrz na ramkę imageView1.- Strona 2: Przewiń i przejdź do strony 2 {x:640,y:0, width : 320, height : 200) Jeszcze raz spójrz na ramkę imageView2
To samo dotyczy przypadku UICollectionView. Najprostszym sposobem na obejrzenie collectionView jest przewinięcie go i wydrukowanie / zapisanie jego granic, a otrzymasz pomysł.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-10 03:08:39
Bounds - x:0, y:0, width: 20, height: 40 is a static
Ramka - x:60, y:20, szerokość: 45, wysokość: 45 jest dynamiką opartą na granicach wewnętrznych.
Jeszcze jedna ilustracja pokazująca różnicę między ramką a obramowaniem. W tym przykładzie:
-
View B
jest podzbioremView A
-
View B
został przeniesiony dox:60, y: 20
-
View B
został obrócony45 degrees
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-20 20:36:55
Frame vs bounds
- jeśli utworzysz Widok W x:0, Y:0, szerokość:400, wysokość: 400, jego ramka a granice są takie same.
- Jeśli przeniesiesz ten widok Do x:400, jego ramka będzie odzwierciedlać tę zmianę, ale jej granice nie będą. Pamiętaj, że granice są w stosunku do własnej przestrzeni widoku, a wewnętrznie do widoku nic się nie zmieniło.
- jeśli przekształcisz widok, np. obracając go lub skalując, ramka zmieni się, aby odzwierciedlić to, ale granice nadal nie będą – jak far as widok jest wewnętrznie zaniepokojony, nie zmienił się.
- jeśli zmienisz obramowanie, zmieni to zawartość wewnątrz ramki, ponieważ początek prostokąta obramowania zaczyna się od inna część widoku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-08 14:34:56