Khung UIView, giới hạn và trung tâm


320

Tôi muốn biết làm thế nào để sử dụng các thuộc tính này đúng cách.

Theo tôi hiểu, framecó thể được sử dụng từ vùng chứa của chế độ xem tôi đang tạo. Nó đặt vị trí khung nhìn tương đối với khung nhìn container. Nó cũng đặt kích thước của khung nhìn đó.

Cũng centercó thể được sử dụng từ vùng chứa của chế độ xem tôi đang tạo. Thuộc tính này thay đổi vị trí của khung nhìn so với vùng chứa của nó.

Cuối cùng, boundslà liên quan đến chính nó. Nó thay đổi khu vực có thể vẽ cho xem.

Bạn có thể cung cấp thêm thông tin về mối quan hệ giữa framebounds? Những gì về clipsToBoundsmasksToBoundstài sản?


1
Thảo luận này đã được giải quyết ở đây. vì vậy, vui lòng xem tại đây hoặc có thể thấy Tài liệu dành cho nhà phát triển được cung cấp tại đây developer.apple.com/l Library / ios / # document / uikit / report / nam
tráng lệ

Câu trả lời:


577

Vì câu hỏi tôi đã hỏi nhiều lần nên tôi sẽ cung cấp câu trả lời chi tiết về nó. Hãy sửa đổi nó nếu bạn muốn thêm nội dung chính xác hơn.

Đầu tiên một bản tóm tắt về câu hỏi: khung, giới hạn và trung tâm và các mối quan hệ của họ.

Khung A view frame( CGRect) là vị trí của hình chữ nhật trong superviewhệ tọa độ của nó. Theo mặc định, nó bắt đầu ở trên cùng bên trái.

Giới hạn Một khung nhìn bounds( CGRect) biểu thị hình chữ nhật khung nhìn trong hệ tọa độ của chính nó.

Trung tâm A centerđược CGPointbiểu thị dưới dạng superviewhệ tọa độ và nó xác định vị trí của điểm trung tâm chính xác của chế độ xem.

Lấy từ vị trí UIView +, đây là các mối quan hệ (chúng không hoạt động theo mã vì chúng là các phương trình không chính thức) trong số các thuộc tính trước đó:

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

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

  • frame.size = bounds.size

LƯU Ý: Các mối quan hệ này không áp dụng nếu các chế độ xem được xoay. Để biết thêm thông tin, tôi sẽ đề nghị bạn xem hình ảnh sau đây được lấy từ The Kitchen Drawer dựa trên khóa học Stanford CS193p . Tín dụng vào @Rhubarb .

Khung, giới hạn và trung tâm

Sử dụng framecho phép bạn định vị lại và / hoặc thay đổi kích thước một khung nhìn trong phạm vi của nó superview. Thông thường có thể được sử dụng từ một superview, ví dụ, khi bạn tạo một khung nhìn cụ thể. Ví dụ:

// 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];

Khi bạn cần tọa độ để vẽ bên trong, viewbạn thường tham khảo bounds. Một ví dụ điển hình có thể là vẽ trong một viewcuộc phỏng vấn dưới dạng một hình nhỏ của cái đầu tiên. Vẽ các cuộc phỏng vấn đòi hỏi phải biết giám boundssát. Ví dụ:

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];

Hành vi khác nhau xảy ra khi bạn thay đổi boundsquan điểm. Ví dụ: nếu bạn thay đổi bounds size, các framethay đổi (và ngược lại). Sự thay đổi xảy ra xung quanh centerquan điểm. Sử dụng mã dưới đây và xem những gì xảy ra:

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

Hơn nữa, nếu bạn thay đổi, bounds originbạn thay đổi originhệ tọa độ bên trong của nó. Theo mặc định, originnó nằm ở (0.0, 0.0)(góc trên cùng bên trái). Ví dụ: nếu bạn thay đổi origincho view1bạn có thể thấy (nhận xét mã trước đó nếu bạn muốn) bây giờ là góc trên cùng bên trái để view2chạm vào mã view1. Động lực khá đơn giản. Bạn nói với view1rằng góc trái trên của nó bây giờ là ở vị trí (20.0, 20.0)nhưng vì view2's frame originbắt đầu từ (20.0, 20.0), họ sẽ trùng.

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

Đại origindiện cho viewvị trí của nó trong vị trí của nó superviewnhưng mô tả vị trí của boundstrung tâm.

Cuối cùng, boundsoriginkhông phải là khái niệm liên quan. Cả hai đều cho phép xuất phát framequan điểm (Xem các phương trình trước).

Nghiên cứu trường hợp của View1

Đây là những gì xảy ra khi sử dụng đoạn mã sau.

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]));

Hình ảnh tương đối.

nhập mô tả hình ảnh ở đây

Điều này thay vì những gì xảy ra nếu tôi thay đổi [self view]giới hạn như sau.

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

Hình ảnh tương đối.

nhập mô tả hình ảnh ở đây

Ở đây bạn nói [self view]rằng góc trên cùng bên trái của nó bây giờ nằm ​​ở vị trí (30.0, 20.0) nhưng vì view1nguồn gốc khung bắt đầu từ (30.0, 20.0), chúng sẽ trùng nhau.

Tài liệu tham khảo bổ sung (để cập nhật với các tài liệu tham khảo khác nếu bạn muốn)

Giới thiệu clipsToBounds(nguồn Apple doc)

Đặt giá trị này thành CÓ làm cho các cuộc phỏng vấn được cắt bớt trong giới hạn của người nhận. Nếu được đặt thành NO, các khung hình có khung hình vượt ra ngoài giới hạn hiển thị của máy thu sẽ không bị cắt bớt. Giá trị mặc định là không.

Nói cách khác, nếu một khung nhìn frame(0, 0, 100, 100)và khung nhìn của nó là (90, 90, 30, 30), bạn sẽ chỉ thấy một phần của khung nhìn đó. Cái sau sẽ không vượt quá giới hạn của quan điểm phụ huynh.

masksToBoundstương đương với clipsToBounds. Thay vào đó UIView, thuộc tính này được áp dụng cho a CALayer. Dưới mui xe, clipsToBoundsgọi masksToBounds. Để tham khảo thêm, hãy xem Làm thế nào là mối quan hệ giữa clipToBound của UIView và mặt nạ ToBound của CALay? .


@Rhubarb Bạn hoàn toàn đúng. Tôi sẽ chỉ ra nó trong câu trả lời của tôi. Cảm ơn.
Lorenzo B

Tôi không biết liệu mình có nhận được câu trả lời hay không nhưng tại sao cuộc phỏng vấn lại di chuyển theo hướng tiêu cực khi nguồn gốc giới hạn được thay đổi. Dường như không thể có được đầu của tôi xung quanh bit đó. Cảm ơn!!
nimgrg

@nimgrg Có vẻ như chế độ xem di chuyển theo hướng tiêu cực nhưng không phải vậy. Các tọa độ bên trong của giám sát được thay đổi.
Lorenzo B

tha thứ cho khả năng hình học của tôi, nếu tọa độ bên trong của giám sát được thay đổi thành gốc (30.0, 20.0), thì điểm (0,0) nằm bên trong hình vuông màu xanh hoặc bên ngoài nó. Tôi chỉ đang cố gắng theo dõi những thay đổi và có ý nghĩa của nó. Cảm ơn đã dành thời gian để trả lời.
nimgrg

@flexaddicted: Xin chào, slide bạn đã thêm nói: "Xem giữa của B trong không gian tọa độ của chính nó là: bound.size. thong / 2 + origin.x" - tại sao lại như vậy? Vì tiêu đề nói trong không gian tọa độ của chính nó , tôi sẽ nói nó là: bound.size. Thong / 2 + bound.origin.x ?? Phải không ??

134

Câu hỏi này đã có một câu trả lời tốt, nhưng tôi muốn bổ sung nó với một số hình ảnh khác. Câu trả lời đầy đủ của tôi là ở đây.

Để giúp tôi nhớ khung hình , tôi nghĩ về một khung hình trên tường . Giống như một bức tranh có thể được di chuyển bất cứ nơi nào trên tường, hệ thống tọa độ của khung nhìn là giám sát. (tường = giám sát, khung = xem)

Để giúp tôi nhớ giới hạn , tôi nghĩ về giới hạn của một sân bóng rổ . Bóng rổ ở đâu đó trong sân giống như hệ thống tọa độ của giới hạn khung nhìn nằm trong tầm nhìn. (tòa án = chế độ xem, bóng rổ / người chơi = nội dung trong chế độ xem)

Giống như khung, view.center cũng nằm trong tọa độ của giám sát.

Khung so với giới hạn - Ví dụ 1

Hình chữ nhật màu vàng đại diện cho khung nhìn. Hình chữ nhật màu xanh lá cây đại diện cho giới hạn của khung nhìn. Dấu chấm màu đỏ trong cả hai hình ảnh đại diện cho nguồn gốc của khung hoặc giới hạn trong các hệ tọa độ của chúng.

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

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

nhập mô tả hình ảnh ở đây


Ví dụ 2

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

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

nhập mô tả hình ảnh ở đây


Ví dụ 3

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

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

nhập mô tả hình ảnh ở đây


Ví dụ 4

Điều này giống như ví dụ 2, ngoại trừ lần này toàn bộ nội dung của chế độ xem được hiển thị vì nó sẽ trông giống như nếu nó không được cắt vào giới hạn của chế độ xem.

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

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

nhập mô tả hình ảnh ở đây


Ví dụ 5

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

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

nhập mô tả hình ảnh ở đây

Một lần nữa, xem ở đây để trả lời của tôi với nhiều chi tiết hơn.


2
Cảm ơn Suragch. Nó thực sự là hình ảnh rất rõ ràng của khung và sự khác biệt ràng buộc. Tôi thực sự đánh giá cao nỗ lực của bạn.
Mohd Haider

Tất cả những thứ mà tôi muốn thấy trong một câu trả lời ngắn gọn và súc tích. Cảm ơn!
Matt Chuang

Tuyệt vời! Ví dụ 3: Tôi không hiểu. bạn chỉ di chuyển nguồn gốc khung hình của mình một chút, làm thế nào để thay đổi xoay hình ảnh của bạn?
Mật ong

@ asma22, Trong ví dụ 3 Tôi chỉ cố gắng chỉ ra rằng khi bạn xoay một hình ảnh, khung hình sẽ thay đổi nhưng giới hạn thì không. Xem câu trả lời đầy đủ hơn của tôi .
Suragch

89

Tôi thấy hình ảnh này hữu ích nhất để hiểu khung, giới hạn, v.v.

nhập mô tả hình ảnh ở đây

Cũng xin lưu ý rằng frame.size != bounds.sizekhi hình ảnh được xoay.


14
Một bức tranh đáng giá ngàn lời nói.
Narendra Kamma

3
Giải thích tốt nhất có thể
Ratikanta Patra

@Erben Mo: Xin chào, slide bạn đã thêm nói: "Xem giữa của B trong không gian tọa độ của chính nó là: bound.size. Thong / 2 + origin.x" - tại sao lại như vậy? Vì tiêu đề nói trong không gian tọa độ của chính nó , tôi sẽ nói nó là: bound.size. Thong / 2 + bound.origin.x ?? Phải không ??

1
Nguồn gốc của hình ảnh này là gì? Liên kết?
David

1
@David Đó là từ khóa học CS193p của Stanford trên iTunesU được tìm thấy ở đây: www.stanford.edu/
class / cs193p / cgi

3

Tôi nghĩ nếu bạn nghĩ nó từ quan điểm CALayer, mọi thứ rõ ràng hơn.

Khung không thực sự là một thuộc tính riêng biệt của khung nhìn hoặc lớp, nó là một thuộc tính ảo, được tính từ giới hạn, vị trí ( UIViewtrung tâm) và biến đổi.

Vì vậy, về cơ bản cách bố trí lớp / khung nhìn thực sự được quyết định bởi ba thuộc tính này (và anchorPoint) và một trong ba thuộc tính này sẽ không thay đổi bất kỳ thuộc tính nào khác, như thay đổi biến đổi không thay đổi giới hạn.


2

Có câu trả lời rất tốt với lời giải thích chi tiết cho bài viết này. Tôi chỉ muốn đề cập rằng có một lời giải thích khác với biểu diễn trực quan cho ý nghĩa của Frame, Bound, Center, Transform, Bound Origin trong video WWDC 2011 Hiểu về UIKit Rendering bắt đầu từ @ 4: 22 đến 20:10


0

Sau khi đọc các câu trả lời ở trên, ở đây thêm các diễn giải của tôi.

Giả sử duyệt trực tuyến, trình duyệt web là của bạn framequyết định vị trí và mức độ lớn để hiển thị trang web. Cuộn của trình duyệt là bạn bounds.originrằng quyết định đó là một phần của trang web sẽ được hiển thị. bounds.originthật khó hiểu Cách tốt nhất để tìm hiểu là tạo Ứng dụng Chế độ xem Đơn, thử sửa đổi các tham số này và xem cách các lượt xem thay đổi.

- (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));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.