Ramka UIView, granice i środek


320

Chciałbym wiedzieć, jak korzystać z tych właściwości we właściwy sposób.

Jak rozumiem, framemożna go używać z kontenera widoku, który tworzę. Ustawia pozycję widoku względem widoku kontenera. Ustawia także rozmiar tego widoku.

Również centermoże być używany z pojemnika widoku tworzę. Ta właściwość zmienia pozycję widoku względem jego kontenera.

Wreszcie boundsodnosi się do samego widoku. Zmienia obszar rysowania widoku.

Czy możesz podać więcej informacji na temat relacji między framei bounds? Co o clipsToBoundsi masksToBoundswłaściwości?


1
Ta dyskusja już tu została rozwiązana. więc proszę, spójrz tutaj lub zobacz dokumentację dla programistów podana tutaj developer.apple.com/library/ios/#documentation/uikit/reference/… stackoverflow.com/questions/1210047/… slideshare.net/onoaonoa/cs193p-lecture-5 -view-animacja
Splendid

Odpowiedzi:


577

Ponieważ pytanie, które zadałem, było wielokrotnie widziane, udzielę szczegółowej odpowiedzi na to pytanie. Zmodyfikuj go, jeśli chcesz dodać więcej poprawnych treści.

Najpierw podsumowanie pytania: ramy, granice i centrum oraz ich relacje.

Ramka Widok frame( CGRect) to pozycja jego prostokąta w układzie superviewwspółrzędnych. Domyślnie zaczyna się w lewym górnym rogu.

Granice Widok bounds( CGRect) wyraża prostokąt widoku we własnym układzie współrzędnych.

Środek A centerjest CGPointwyrażony w kategoriach superviewukładu współrzędnych i określa położenie dokładnie środkowego punktu widoku.

Wzięte z pozycji UIView + są to relacje (nie działają w kodzie, ponieważ są nieformalnymi równaniami) wśród poprzednich właściwości:

  • frame.origin = center - (bounds.size / 2.0)

  • center = frame.origin + (bounds.size / 2.0)

  • frame.size = bounds.size

UWAGA: Te relacje nie mają zastosowania, jeśli widoki są obracane. Aby uzyskać więcej informacji, zasugeruję, abyś spojrzał na poniższe zdjęcie zrobione z The Kitchen Drawer na podstawie kursu Stanford CS193p . Kredyty trafiają do @ Rabarbaru .

Rama, granice i środek

Korzystanie z frameumożliwia zmianę położenia i / lub zmianę rozmiaru widoku w jego obrębie superview. Zwykle można go użyć superviewna przykład podczas tworzenia określonego widoku podrzędnego. Na przykład:

// view1 will be positioned at x = 30, y = 20 starting the top left corner of [self view]
// [self view] could be the view managed by a UIViewController
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)];    
view1.backgroundColor = [UIColor redColor];

[[self view] addSubview:view1];

Kiedy potrzebujesz współrzędnych do rysowania w środku view, zwykle się do niego odwołujesz bounds. Typowym przykładem może być narysowanie w viewwidoku częściowym jako wstawka pierwszego. Narysowanie rzutu podrzędnego wymaga znajomości boundssuperwizji. Na przykład:

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 400.0f, 400.0f)];    
view1.backgroundColor = [UIColor redColor];

UIView* view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];    
view2.backgroundColor = [UIColor yellowColor];

[view1 addSubview:view2];

Różne zachowania występują po zmianie boundswidoku. Na przykład, jeśli zmienisz bounds size, framezmiany (i odwrotnie). Zmiana następuje wokół centerwidoku. Użyj poniższego kodu i zobacz, co się stanie:

NSLog(@"Old Frame %@", NSStringFromCGRect(view2.frame));
NSLog(@"Old Center %@", NSStringFromCGPoint(view2.center));    

CGRect frame = view2.bounds;
frame.size.height += 20.0f;
frame.size.width += 20.0f;
view2.bounds = frame;

NSLog(@"New Frame %@", NSStringFromCGRect(view2.frame));
NSLog(@"New Center %@", NSStringFromCGPoint(view2.center));

Ponadto, jeśli zmienisz bounds origin, zmienisz originjego wewnętrzny układ współrzędnych. Domyślnie originjest w (0.0, 0.0)(lewy górny róg). Na przykład, jeśli zmienisz origindla view1widać (komentarz poprzedni kod, jeśli chcesz), że teraz w lewym górnym rogu na view2dotknie view1jeden. Motywacja jest dość prosta. Mówisz view1, że jej lewym górnym rogu znajduje się teraz w położeniu (20.0, 20.0), ale ponieważ view2„s frame originzaczyna się od (20.0, 20.0), będą zbieżne.

CGRect frame = view1.bounds;
frame.origin.x += 20.0f;
frame.origin.y += 20.0f;
view1.bounds = frame; 

originReprezentuje view„S pozycję w obrębie swojego superviewale określa położenie boundsśrodka.

Wreszcie boundsi originnie są powiązanymi pojęciami. Oba pozwalają na wyprowadzenie framewidoku (patrz poprzednie równania).

Studium przypadku View1

Oto, co dzieje się podczas korzystania z następującego fragmentu kodu.

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)];
view1.backgroundColor = [UIColor redColor];

[[self view] addSubview:view1];

NSLog(@"view1's frame is: %@", NSStringFromCGRect([view1 frame]));
NSLog(@"view1's bounds is: %@", NSStringFromCGRect([view1 bounds]));
NSLog(@"view1's center is: %@", NSStringFromCGPoint([view1 center]));

Względny obraz.

wprowadź opis zdjęcia tutaj

To zamiast tego dzieje się, jeśli zmienię [self view]granice w następujący sposób.

// previous code here...
CGRect rect = [[self view] bounds];
rect.origin.x += 30.0f;
rect.origin.y += 20.0f;
[[self view] setBounds:rect];

Względny obraz.

wprowadź opis zdjęcia tutaj

Mówisz tutaj, [self view]że jego lewy górny róg jest teraz w pozycji (30,0, 20,0), ale ponieważ view1początek ramki zaczyna się od (30,0, 20,0), będą one zbieżne.

Dodatkowe odniesienia (w razie potrzeby zaktualizuj o inne odniesienia)

Informacje clipsToBounds(źródłowy dokument Apple)

Ustawienie tej wartości na TAK powoduje przycięcie widoków podrzędnych do granic odbiornika. Jeśli ustawione na NIE, widoki podrzędne, których ramki wykraczają poza widoczne granice odbiornika, nie są przycinane. Wartość domyślna to NIE.

Innymi słowy, jeśli widok framejest, (0, 0, 100, 100)a jego widok podrzędny jest (90, 90, 30, 30), zobaczysz tylko część tego widoku podrzędnego. Ten ostatni nie przekroczy granic widoku nadrzędnego.

masksToBoundsjest równoważne z clipsToBounds. Zamiast UIViewtego właściwość ta jest stosowana do CALayer. Pod maską clipsToBoundsnazywa masksToBounds. W celu uzyskania dalszych odniesień zapoznaj się z relacją między klipami UIView clipsToBounds i CALayer masksToBounds? .


@ Rabarbar Masz całkowitą rację. Wskażę to w mojej odpowiedzi. Dzięki.
Lorenzo B

Nie wiem, czy dostanę odpowiedź, ale dlaczego widok podrzędny przesuwa się w kierunku ujemnym, gdy zmienia się początek granic. Wydaje mi się, że nie potrafię się tym zająć. Dzięki!!
nimgrg,

@nimgrg Wygląda na to, że widok porusza się w kierunku ujemnym, ale tak nie jest. Współrzędne wewnętrzne superwizji zostały zmienione.
Lorenzo B,

wybaczcie moje zdolności geometryczne, jeśli wewnętrzne współrzędne superwizji zostaną zmienione na początek (30,0, 20,0), czy punkt (0,0) leży wewnątrz niebieskiego kwadratu lub poza nim. Próbuję tylko wyśledzić zmiany i je zrozumieć. Dzięki za poświęcenie czasu na odpowiedź.
nimgrg,

@flexaddicted: Cześć, dodany slajd mówi: „Widok środka B we własnej przestrzeni współrzędnych to: bounds.size.width / 2 + origin.x” - dlaczego tak jest? Ponieważ tytuł mówi we własnej przestrzeni współrzędnych, powiedziałbym, że jest: bounds.size.width / 2 + bounds.origin.x ?? Prawda?

134

To pytanie ma już dobrą odpowiedź, ale chcę je uzupełnić kilkoma zdjęciami. Moja pełna odpowiedź jest tutaj.

Aby pomóc mi zapamiętać ramkę , myślę o ramce na ścianie . Podobnie jak obraz można przenieść w dowolne miejsce na ścianie, układ współrzędnych ramki widoku jest superwizorem. (ściana = superview, frame = view)

Aby pomóc mi zapamiętać granice , myślę o granicach boiska do koszykówki . Koszykówka jest gdzieś na boisku, tak jak układ współrzędnych granic widoku znajduje się w samym widoku. (boisko = widok, koszykówka / zawodnicy = treść w widoku)

View.center , podobnie jak ramka, również znajduje się we współrzędnych superwizji.

Frame vs Bounds - Przykład 1

Żółty prostokąt reprezentuje ramkę widoku. Zielony prostokąt reprezentuje granice widoku. Czerwona kropka na obu obrazach reprezentuje początek ramki lub granic w ich układach współrzędnych.

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

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

wprowadź opis zdjęcia tutaj


Przykład 2

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

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

wprowadź opis zdjęcia tutaj


Przykład 3

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

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

wprowadź opis zdjęcia tutaj


Przykład 4

Jest to to samo, co w przykładzie 2, z tym wyjątkiem, że tym razem wyświetlana jest cała zawartość widoku, tak jakby wyglądał, gdyby nie został przycięty do granic widoku.

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

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

wprowadź opis zdjęcia tutaj


Przykład 5

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

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

wprowadź opis zdjęcia tutaj

Ponownie zobacz tutaj moją odpowiedź z dalszymi szczegółami.


2
Dzięki, Suragch. To naprawdę bardzo wyraźny obraz różnicy ramek i oprawek. Naprawdę doceniam twój wysiłek.
Mohd Haider

Wszystkie rzeczy, które chcę zobaczyć w jednej krótkiej i zwięzłej odpowiedzi. Dzięki!
Matt Chuang

Niesamowite! Na przykład 3: Nie rozumiem. tylko nieznacznie przesunąłeś początek ramki, jak to zmienia obrót twojego obrazu?
Honey

@ asma22, W przykładzie 3 próbowałem tylko pokazać, że kiedy obracasz obraz, ramka się zmienia, ale granice się nie zmieniają. Zobacz moją pełniejszą odpowiedź .
Suragch 16.04.16

89

Uważam, że ten obraz jest najbardziej pomocny w zrozumieniu ramki, granic itp.

wprowadź opis zdjęcia tutaj

Należy również pamiętać, że frame.size != bounds.sizepo obróceniu obrazu.


14
Obraz wart tysiąca słów.
Narendra Kamma

3
Najlepsze możliwe wytłumaczenie
Ratikanta Patra

@Erben Mo: Cześć, dodany slajd mówi: „Widok środka B we własnej przestrzeni współrzędnych to: bounds.size.width / 2 + origin.x” - dlaczego tak jest? Ponieważ tytuł mówi we własnej przestrzeni współrzędnych, powiedziałbym, że jest: bounds.size.width / 2 + bounds.origin.x ?? Prawda?

1
Jakie jest źródło tego obrazu? Połączyć?
David

1
@David To z kursu Stanford CS193p na iTunesU można znaleźć tutaj: www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2013-fall
preynolds

3

Myślę, że jeśli myślisz od samego początku CALayer, wszystko jest bardziej jasne.

Ramka nie jest tak naprawdę odrębną właściwością widoku ani warstwy, jest to właściwość wirtualna, obliczona na podstawie granic, pozycji ( UIViewcentrum) i transformacji.

Tak więc w zasadzie sposób, w jaki układ warstw / widoku jest tak naprawdę decydowany przez te trzy właściwości (i anchorPoint), i żadna z tych trzech właściwości nie zmieni żadnej innej właściwości, tak jak zmiana transformacji nie zmienia granic.


2

Istnieją bardzo dobre odpowiedzi ze szczegółowym wyjaśnieniem tego postu. Chciałbym tylko odnieść się do tego, że istnieje inne wyjaśnienie z wizualną reprezentacją znaczenia Frame, Bounds, Center, Transform, Bounds Origin w WWDC 2011 wideo Zrozumienie renderowania UIKit od @ 4: 22 do 20:10


0

Po przeczytaniu powyższych odpowiedzi dodam tutaj moje interpretacje.

Załóżmy, przeglądania stron internetowych, przeglądarka internetowa jest twój frame, który decyduje, gdzie i jak duży, aby pokazać stronę internetową. Scroller przeglądarki bounds.origindecyduje, która część strony zostanie wyświetlona.bounds.originjest trudny do zrozumienia. Najlepszym sposobem na nauczenie się jest tworzenie aplikacji z pojedynczym widokiem, próba modyfikacji tych parametrów i sprawdzenie, jak zmieniają się widoki podrzędne.

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100.0f, 200.0f, 200.0f, 400.0f)];
[view1 setBackgroundColor:[UIColor redColor]];

UIView *view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];
[view2 setBackgroundColor:[UIColor yellowColor]];
[view1 addSubview:view2];

[[self view] addSubview:view1];

NSLog(@"Old view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center));
NSLog(@"Old view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));

// Modify this part.
CGRect bounds = view1.bounds;
bounds.origin.x += 10.0f;
bounds.origin.y += 10.0f;

// incase you need width, height
//bounds.size.height += 20.0f;
//bounds.size.width += 20.0f;

view1.bounds = bounds;

NSLog(@"New view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center));
NSLog(@"New view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.