กรอบ UIView ขอบเขตและศูนย์กลาง


320

ฉันต้องการทราบวิธีใช้คุณสมบัติเหล่านี้อย่างถูกวิธี

ดังที่ฉันเข้าใจframeสามารถใช้จากคอนเทนเนอร์ของมุมมองที่ฉันสร้าง มันกำหนดตำแหน่งมุมมองเทียบกับมุมมองภาชนะ นอกจากนี้ยังกำหนดขนาดของมุมมองนั้น

นอกจากนี้ยังcenterสามารถนำมาใช้จากภาชนะของมุมมองที่ฉันสร้าง คุณสมบัตินี้เปลี่ยนตำแหน่งของมุมมองที่สัมพันธ์กับคอนเทนเนอร์

ในที่สุดboundsก็สัมพันธ์กับมุมมองของตัวเอง มันเปลี่ยนพื้นที่ drawable สำหรับมุมมอง

คุณสามารถให้ข้อมูลเพิ่มเติมเกี่ยวกับความสัมพันธ์ระหว่างframeและbounds? สิ่งที่เกี่ยวกับclipsToBoundsและmasksToBoundsคุณสมบัติ?


1
การสนทนานี้แก้ไขแล้วที่นี่ ดังนั้นกรุณาดูได้ที่นี่หรือจะได้เห็นเอกสารนักพัฒนาให้ที่นี่developer.apple.com/library/ios/#documentation/uikit/reference/... stackoverflow.com/questions/1210047/... slideshare.net/onoaonoa/cs193p-lecture-5 -view-animation
Splendid

คำตอบ:


577

ตั้งแต่คำถามที่ฉันถามถูกพบหลายครั้งฉันจะให้คำตอบโดยละเอียดของมัน อย่าลังเลที่จะแก้ไขหากคุณต้องการเพิ่มเนื้อหาที่ถูกต้องมากขึ้น

ก่อนอื่นสรุปคำถาม: กรอบขอบเขตและศูนย์และความสัมพันธ์ของพวกเขา

มุมมองเฟรม A frame( CGRect) คือตำแหน่งของสี่เหลี่ยมมุมฉากในsuperviewระบบพิกัดของ โดยค่าเริ่มต้นมันเริ่มต้นที่ด้านบนซ้าย

ขอบเขตมุมมองbounds( CGRect) แสดงมุมมองสี่เหลี่ยมผืนผ้าในระบบพิกัดของตัวเอง

ศูนย์ A centerเป็นระบบพิกัดที่CGPointแสดงออกมาในรูปsuperviewแบบและจะกำหนดตำแหน่งของจุดศูนย์กลางที่แน่นอนของมุมมอง

นำมาจากตำแหน่ง UIView +เหล่านี้คือความสัมพันธ์ (พวกเขาไม่ทำงานในรหัสเนื่องจากเป็นสมการที่ไม่เป็นทางการ) ในคุณสมบัติก่อนหน้านี้:

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

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

  • frame.size = bounds.size

หมายเหตุ:ความสัมพันธ์เหล่านี้ใช้ไม่ได้หากมุมมองหมุน สำหรับข้อมูลเพิ่มเติมฉันจะแนะนำให้คุณดูภาพต่อไปนี้ที่ถ่ายจากThe Kitchen DrawerตามหลักสูตรของStanford CS193p เครดิตไป@Rhubarb

กรอบขอบเขตและศูนย์กลาง

ใช้frameช่วยให้คุณเพื่อเปลี่ยนตำแหน่งและ / superviewหรือปรับขนาดมุมมองภายในของมัน โดยปกติจะสามารถใช้จากsuperviewตัวอย่างเช่นเมื่อคุณสร้างมุมมองย่อยที่เฉพาะเจาะจง ตัวอย่างเช่น:

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

เมื่อคุณต้องการพิกัดที่จะวาดภาพภายในคุณมักจะหมายถึงview boundsตัวอย่างทั่วไปสามารถวาดภายในมุมviewมองย่อยเป็นสิ่งที่ใส่เข้าไปของภาพแรก การวาดมุมมองย่อยนั้นจำเป็นต้องรู้boundsของผู้ดูแล ตัวอย่างเช่น:

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

พฤติกรรมที่แตกต่างเกิดขึ้นเมื่อคุณเปลี่ยนboundsมุมมอง ตัวอย่างเช่นหากคุณเปลี่ยนbounds sizeการframeเปลี่ยนแปลง (และในทางกลับกัน) การเปลี่ยนแปลงเกิดขึ้นรอบ ๆcenterมุมมอง ใช้รหัสด้านล่างและดูว่าเกิดอะไรขึ้น:

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

นอกจากนี้หากคุณเปลี่ยนbounds originคุณจะเปลี่ยนoriginระบบพิกัดภายใน โดยค่าเริ่มต้นoriginคือที่(0.0, 0.0)(มุมซ้ายบน) ตัวอย่างเช่นหากคุณเปลี่ยนoriginเพื่อให้view1คุณสามารถเห็น (แสดงความคิดเห็นรหัสก่อนหน้าถ้าคุณต้องการ) ว่าตอนนี้มุมซ้ายบนสำหรับview2สัมผัสview1หนึ่ง แรงจูงใจค่อนข้างง่าย คุณบอกว่าไปview1ที่มุมบนซ้ายของตนในขณะนี้คือที่ตำแหน่ง(20.0, 20.0)แต่เนื่องจากview2's frame originเริ่มต้นที่(20.0, 20.0)พวกเขาจะตรง

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

originหมายถึงviewตำแหน่งของภายในของมันsuperviewแต่อธิบายตำแหน่งของboundsศูนย์

ในที่สุดboundsและoriginไม่ได้เป็นแนวคิดที่เกี่ยวข้อง ทั้งคู่อนุญาตให้สืบทอดframeมุมมอง (ดูสมการก่อนหน้า)

กรณีศึกษาของ View1

นี่คือสิ่งที่เกิดขึ้นเมื่อใช้ตัวอย่างต่อไปนี้

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

รูปภาพที่สัมพันธ์กัน

ป้อนคำอธิบายรูปภาพที่นี่

สิ่งนี้จะเกิดอะไรขึ้นถ้าฉันเปลี่ยน[self view]ขอบเขตดังต่อไปนี้

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

รูปภาพที่สัมพันธ์กัน

ป้อนคำอธิบายรูปภาพที่นี่

ที่นี่คุณบอก[self view]ว่ามุมซ้ายบนตอนนี้อยู่ที่ตำแหน่ง (30.0, 20.0) แต่เนื่องจากview1จุดเริ่มต้นของเฟรมเริ่มต้นจาก (30.0, 20.0) พวกเขาจะตรงกัน

การอ้างอิงเพิ่มเติม (เพื่ออัปเดตด้วยการอ้างอิงอื่น ๆ หากคุณต้องการ)

เกี่ยวกับclipsToBounds(แหล่งเอกสารของ Apple)

การตั้งค่านี้เป็น YES จะทำให้การแสดงตัวอย่างถูก จำกัด ขอบเขตของผู้รับ หากตั้งค่าเป็น NO ภาพย่อยที่เฟรมขยายเกินขอบเขตที่มองเห็นได้ของผู้รับจะไม่ถูกตัดออก ค่าเริ่มต้นคือ NO

กล่าวอีกนัยหนึ่งถ้ามุมมองframeเป็น(0, 0, 100, 100)และมุมมองย่อยคือ(90, 90, 30, 30)คุณจะเห็นเพียงส่วนหนึ่งของมุมมองย่อยนั้น หลังจะไม่เกินขอบเขตของมุมมองหลัก

masksToBoundsclipsToBoundsเทียบเท่ากับ แทนที่จะไปคุณสมบัตินี้ถูกนำไปใช้กับUIView CALayerภายใต้ประทุน, โทรclipsToBounds masksToBoundsสำหรับการอ้างอิงเพิ่มเติมดูที่ความสัมพันธ์ระหว่าง UOBD ของ ClipToBounds กับ CAL ของมาสก์ได้อย่างไร .


@Rubarb คุณพูดถูก ฉันจะชี้ให้เห็นในคำตอบของฉัน ขอบคุณ
Lorenzo B

ฉันไม่รู้ว่าฉันจะได้รับคำตอบหรือไม่ แต่ทำไมมุมมองย่อยย้ายไปในทิศทางลบเมื่อต้นกำเนิดขอบเขตเปลี่ยนไป ดูเหมือนจะไม่ได้รับหัวของฉันรอบที่บิต ขอบคุณ !!
nimgrg

@nimgrg ดูเหมือนว่าภาพเคลื่อนไหวไปในทิศทางลบ แต่ไม่ใช่ พิกัดภายในของ superview มีการเปลี่ยนแปลง
Lorenzo B

ยกโทษให้ความสามารถทางเรขาคณิตของฉันถ้าพิกัดภายในของ superview เปลี่ยนเป็นจุดเริ่มต้น (30.0, 20.0) จุด (0,0) อยู่ในสี่เหลี่ยมสีน้ำเงินหรือด้านนอกหรือไม่ ฉันแค่พยายามติดตามการเปลี่ยนแปลงและทำความเข้าใจกับมัน ขอบคุณที่สละเวลาตอบ
nimgrg

@flexaddicted: สวัสดีสไลด์ที่คุณเพิ่มพูดว่า: "ดู B ตรงกลางในพื้นที่พิกัดของตัวเองคือ: bounds.size.width / 2 + origin.x" - ทำไมเป็นเช่นนี้? เนื่องจากชื่อกล่าวว่าในพื้นที่ประสานงานของตัวเองฉันจะบอกว่ามันคือ: bounds.size.width / 2 + bounds.origin.x ?? ไม่ใช่เหรอ

134

คำถามนี้มีคำตอบที่ดีอยู่แล้ว แต่ฉันต้องการเสริมด้วยรูปภาพเพิ่มเติม คำตอบทั้งหมดของฉันอยู่ที่นี่

เพื่อช่วยให้ฉันจำกรอบฉันคิดถึงกรอบรูปบนผนังกรอบรูปบนผนังเช่นเดียวกับภาพที่สามารถเคลื่อนย้ายได้ทุกที่บนผนังระบบพิกัดของเฟรมมุมมองคือกำกับ (wall = superview, frame = view)

เพื่อช่วยให้ฉันจำขอบเขตได้ฉันคิดถึงขอบเขตของสนามบาสเกตบอลขอบเขตของสนามบาสเกตบอลบาสเก็ตบอลอยู่ที่ไหนสักแห่งในสนามเช่นเดียวกับระบบพิกัดของขอบเขตของมุมมองที่อยู่ในมุมมองของตัวเอง (ศาล = มุมมอง, บาสเก็ตบอล / ผู้เล่น = เนื้อหาในมุมมอง)

เช่นเดียวกับเฟรมview.centerก็อยู่ในพิกัดของ superview ด้วย

Frame vs Bounds - ตัวอย่างที่ 1

สี่เหลี่ยมสีเหลืองแสดงถึงเฟรมของมุมมอง สี่เหลี่ยมผืนผ้าสีเขียวแสดงถึงขอบเขตของมุมมอง จุดสีแดงในรูปภาพทั้งสองแสดงถึงจุดเริ่มต้นของเฟรมหรือขอบเขตภายในระบบพิกัด

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

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

ป้อนคำอธิบายรูปภาพที่นี่


ตัวอย่างที่ 2

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

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

ป้อนคำอธิบายรูปภาพที่นี่


ตัวอย่างที่ 3

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

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

ป้อนคำอธิบายรูปภาพที่นี่


ตัวอย่างที่ 4

นี่เป็นเช่นเดียวกับตัวอย่าง 2 ยกเว้นเวลานี้เนื้อหาทั้งหมดของมุมมองจะปรากฏตามที่ดูเหมือนว่าจะไม่ถูกตัดกับขอบเขตของมุมมอง

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

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

ป้อนคำอธิบายรูปภาพที่นี่


ตัวอย่างที่ 5

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

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

ป้อนคำอธิบายรูปภาพที่นี่

ดูคำตอบของฉันพร้อมรายละเอียดเพิ่มเติมที่นี่อีกครั้ง


2
ขอบคุณ Suragch มันเป็นภาพที่ชัดเจนของกรอบและความแตกต่างที่ถูกผูกไว้ ฉันซาบซึ้งในความพยายามของคุณ
Mohd Haider

ทุกสิ่งที่ฉันต้องการเห็นในคำตอบสั้น ๆ และกระชับ ขอบคุณ!
Matt Chuang

! น่ากลัว ตัวอย่างที่ 3: ฉันไม่เข้าใจ คุณย้ายต้นกำเนิดเฟรมของคุณเพียงเล็กน้อยแล้วมันเปลี่ยนการหมุนของภาพอย่างไร
ฮันนี่

@ asma22 ในตัวอย่างที่ 3 ฉันแค่พยายามแสดงให้เห็นว่าเมื่อคุณหมุนรูปภาพเฟรมจะเปลี่ยน แต่ขอบเขตไม่ได้ ดูคำตอบของฉันฟูลเลอร์
Suragch

89

ฉันพบว่ารูปภาพนี้มีประโยชน์มากที่สุดสำหรับการทำความเข้าใจกรอบขอบเขตและอื่น ๆ

ป้อนคำอธิบายรูปภาพที่นี่

นอกจากนี้โปรดทราบว่าframe.size != bounds.sizeเมื่อภาพหมุน


14
รูปภาพมูลค่าหนึ่งพันคำ
Narendra Kamma

3
คำอธิบายที่ดีที่สุด
Ratikanta Patra

@Erben Mo: สวัสดีสไลด์ที่คุณเพิ่มพูดว่า: "ดู B ตรงกลางในพื้นที่พิกัดของตัวเองคือ: bounds.size.width / 2 + origin.x" - ทำไมเป็นเช่นนี้? เนื่องจากชื่อกล่าวว่าในพื้นที่ประสานงานของตัวเองฉันจะบอกว่ามันคือ: bounds.size.width / 2 + bounds.origin.x ?? ไม่ใช่เหรอ

1
แหล่งที่มาของภาพนี้คืออะไร? Link?
David

1
@David มาจากหลักสูตร CS193p ของ Stanford ใน iTunesU พบได้ที่นี่: www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2013-fall
preynolds

3

ฉันคิดว่าถ้าคุณคิดว่ามันCALayerชัดเจนทุกอย่างชัดเจนมากขึ้น

Frame ไม่ได้เป็นคุณสมบัติที่แตกต่างกันอย่างแท้จริงของมุมมองหรือเลเยอร์เลยมันเป็นคุณสมบัติเสมือนที่คำนวณจากขอบเขตตำแหน่ง (UIViewศูนย์กลางของศูนย์) และการแปลง

ดังนั้นโดยทั่วไปแล้วเลเยอร์ / มุมมองเลย์เอาต์จะถูกตัดสินโดยสามคุณสมบัตินี้ (และ anchorPoint) และทั้งสามคุณสมบัตินี้จะไม่เปลี่ยนคุณสมบัติอื่นใดเช่นการเปลี่ยนการเปลี่ยนแปลงไม่ได้เปลี่ยนแปลงขอบเขต


2

มีคำตอบที่ดีมากพร้อมคำอธิบายโดยละเอียดเกี่ยวกับโพสต์นี้ ฉันแค่อยากจะอ้างว่ามีคำอธิบายอื่นที่มีการแสดงออกด้วยภาพสำหรับความหมายของ Frame, Bounds, Centre, Transform, Bounds Origin ในวิดีโอ WWDC 2011 การทำความเข้าใจกับ UIKit Renderingเริ่มตั้งแต่ @ 4: 22 จนถึง 20:10


0

หลังจากอ่านคำตอบข้างต้นแล้วที่นี่เพิ่มการตีความของฉัน

สมมติว่าคุณกำลังใช้งานออนไลน์เว็บเบราว์เซอร์เป็นตัวframeตัดสินว่าจะแสดงหน้าเว็บที่ไหนและขนาดไหน Scrollerของเบราว์เซอร์เป็นของคุณbounds.originที่จะตัดสินใจว่าจะแสดงส่วนใดของหน้าเว็บ bounds.originยากที่จะเข้าใจ วิธีที่ดีที่สุดในการเรียนรู้คือการสร้างแอปพลิเคชันมุมมองเดียวลองปรับเปลี่ยนพารามิเตอร์เหล่านี้และดูว่าการเปลี่ยนหัวข้อย่อยเป็นอย่างไร

- (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));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.