ฉันจะรับความกว้างและความสูงปัจจุบันของมุมมองได้อย่างไรเมื่อใช้ข้อ จำกัด การจัดวางอัตโนมัติ


108

ฉันไม่ได้พูดถึงคุณสมบัติของเฟรมเพราะจากนั้นคุณจะได้ขนาดของมุมมองใน xib เท่านั้น ฉันกำลังพูดถึงเมื่อมุมมองถูกปรับขนาดเนื่องจากข้อ จำกัด (อาจจะหลังการหมุนหรือเพื่อตอบสนองต่อเหตุการณ์) มีวิธีรับความกว้างและความสูงปัจจุบันหรือไม่?

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

ทำไมเรื่องนี้ถึงยากสำหรับฉัน ARG!


ฉันคิดว่าขอบเขตของมุมมองในช่วงเวลาใดเวลาหนึ่งมีความกว้างและความสูงในปัจจุบัน นั่นคือสิ่งที่ได้รับการปรับเปลี่ยนในระหว่างกระบวนการจัดวาง
Phillip Mills

ฉันไม่เคยคิดที่จะตรวจสอบขอบเขต ฉันจะทำตอนนี้
yuf

คำตอบ:


246

คำตอบคือ[view layoutIfNeeded].

นี่คือเหตุผล:

คุณยังคงได้รับความกว้างและความสูงในปัจจุบันของมุมมองโดยการตรวจสอบview.bounds.size.widthและview.bounds.size.height(หรือเฟรมซึ่งเทียบเท่าเว้นแต่คุณจะเล่นด้วยview.transform)

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

คุณขอให้เค้าโครงอัตโนมัติอัปเดตเค้าโครงได้อย่างไร โทร[view setNeedsLayout]ถ้าคุณต้องการให้เค้าโครงอัตโนมัติอัปเดตเค้าโครงในรอบถัดไปของการวนรอบการทำงาน

แต่ถ้าคุณต้องการที่จะปรับปรุงรูปแบบทันทีดังนั้นทันทีที่คุณสามารถเข้าถึงขอบเขตใหม่ให้คุณค่าต่อมาภายในฟังก์ชั่นปัจจุบันของคุณหรือที่จุดอื่นก่อนหันของวงวิ่งแล้วคุณจะต้องเรียกและ[view setNeedsLayout][view layoutIfNeeded]

คุณถามคำถามที่สอง: "ฉันจะเปลี่ยนข้อ จำกัด ด้านความสูง / ความกว้างได้อย่างไรหากฉันไม่มีข้อมูลอ้างอิงโดยตรง"

หากคุณสร้างข้อ จำกัด ใน IB ทางออกที่ดีที่สุดคือสร้าง IBOutlet ในตัวควบคุมมุมมองหรือมุมมองของคุณเพื่อให้คุณมีการอ้างอิงโดยตรง หากคุณสร้างข้อ จำกัด ในโค้ดคุณควรยึดการอ้างอิงในคุณสมบัติที่อ่อนแอภายในในเวลาที่คุณสร้างขึ้น หากมีคนอื่นสร้างข้อ จำกัด คุณจะต้องค้นหาโดยการตรวจสอบคุณสมบัติ view.constraints บนมุมมองและอาจเป็นลำดับชั้นของมุมมองทั้งหมดและใช้ตรรกะที่พบ NSLayoutConstraint ที่สำคัญ นี่อาจเป็นวิธีที่ไม่ถูกต้องเนื่องจากยังกำหนดให้คุณต้องพิจารณาว่าข้อ จำกัด ใดกำหนดขนาดขอบเขตได้อย่างมีประสิทธิภาพเมื่อไม่มีการรับประกันว่าจะเป็นคำตอบง่ายๆสำหรับคำถามนั้น ค่าขอบเขตสุดท้ายอาจเป็นทางออกของระบบที่ซับซ้อนมากของข้อ จำกัด หลายประการ


16
ที่ดีคำตอบและอธิบายกันมากเกินไป ช่วยฉันไว้หลังจากดึงผมออกมาหนึ่งหรือสองชั่วโมง
Ben Kreeger

2
layoutIfNeededดีมากและจะทำให้เฟรมพร้อมใช้งานทันที อย่างไรก็ตามมันจะบังคับให้มีการแสดงข้อ จำกัด ของมุมมองทั้งหมดในแผนผังย่อยของมุมมองที่เรียกใช้ด้วย ตัวอย่างเช่นหากคุณกำลังเพิ่มมุมมองแบบเป็นโปรแกรมและเรียกดูlayoutIfNeededทั้งหมดในกิจวัตรแบบวนซ้ำของคุณคุณอาจพบว่าลำดับชั้นมุมมองของคุณแสดงผลช้ามาก (ฉันเรียนรู้วิธีนี้อย่างยากลำบาก) ดังที่ได้กล่าวไว้ในคำตอบที่ยอดเยี่ยม 'setNeedsLayout' นั้นมีประสิทธิภาพมากกว่าและจะทำให้เฟรมพร้อมใช้งานในเลย์เอาต์ถัดไปซึ่งจะเกิดขึ้นในเวลาประมาณ 1/60 ของวินาที
shmim

1
ฉันรู้ว่ามันเก่า แต่คุณเรียกวิธีนี้ที่ไหน? ในinitWithCoder?
MayNotBe

4
ทำไมต้องบังคับเค้าโครงเพื่อให้ได้ขอบเขต? ดูเหมือนจะไม่มีประสิทธิภาพและด้วยอินเทอร์เฟซที่ซับซ้อนอาจมีราคาแพง เข้าถึงขอบเขตล่าสุดจาก viewDidLayoutSubviews หรือแม้แต่ viewWillLayoutSubviews แทนและปล่อยให้โกโก้จัดการกับเวลาเลย์เอาต์ของตัวเอง ตั้งค่าคุณสมบัติหากคุณต้องการเข้าถึงค่าหรือแฟล็กเพื่อหลีกเลี่ยงการโทรหลายครั้งหากเป็นปัญหา
smileBot

1
ใช่คุณจะต้องบังคับเค้าโครงก็ต่อเมื่อคุณต้องการเข้าถึงค่าที่คำนวณเค้าโครงอัตโนมัติก่อนที่จะถึงรอบการรัน - ตัวอย่างเช่นภายในขอบเขตของการเรียกใช้ฟังก์ชันปัจจุบัน
algal

8

ฉันมีปัญหาที่คล้ายกันซึ่งฉันต้องการเพิ่มเส้นขอบด้านบนและด้านล่างให้กับUITableViewที่ปรับขนาดตามการตั้งค่าข้อ จำกัด ในไฟล์UIStoryboard. ฉันสามารถเข้าถึงข้อ จำกัด ที่อัปเดตด้วย- (void)viewDidLayoutSubviewsไฟล์. สิ่งนี้มีประโยชน์โดยที่คุณไม่จำเป็นต้องซับคลาสมุมมองและแทนที่วิธีการจัดวาง

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

โดยไม่ต้องเรียกใช้เมธอดจากviewDidLayoutSubviewเมธอดเพียงเส้นขอบด้านบนเท่านั้นที่จะถูกวาดอย่างถูกต้องเนื่องจากเส้นขอบด้านล่างอยู่ที่ไหนสักแห่งนอกหน้าจอ


3
viewDidLayoutSubview ถูกเรียกไม่กี่ครั้งคุณกำลังสร้างเลเยอร์จำนวนมาก ... คุณต้องเพิ่มการตรวจสอบการมีอยู่ก่อนที่จะเพิ่มเลเยอร์อื่น
Kalzem

แสดงความคิดเห็นช้าไปหลายปี ... คุณควรเรียกวิธีนี้ว่าซูเปอร์คลาสเมื่อแทนที่ก่อนสิ่งอื่นใด:[super viewDidLayoutSubviews];
Alejandro Iván

5

สำหรับผู้ที่อาจยังคงประสบปัญหาดังกล่าวโดยเฉพาะกับ TableviewCell

เพียงแค่แทนที่วิธีการ:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

ในกรณีของ UITableViewCell หรือ UICollectionViewCell ให้สร้างคลาสย่อยของเซลล์และแทนที่วิธีการเดียวกัน:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

นี่เป็นวิธีเดียวที่ฉันจะได้ขนาดสุดท้ายที่แท้จริงของมุมมองของฉัน
Benoit Jadinon

นี่เป็นวิธีเดียวที่ฉันจะได้ขนาดสุดท้ายสำหรับมุมมองที่มีข้อ จำกัด ของฉันเพราะก่อนที่ฉันจะพยายามทำให้พวกเขาอยู่ในรายการ wakeFromNib ซึ่งไม่ได้ให้เวลามุมมองย่อยในการปรับขนาดจริงก่อน ขอบคุณ!
Azin Mehrnoosh

3

ใช้-(void)viewWillAppear:(BOOL)animatedและโทร [self.view layoutIfNeeded];- ได้ผลฉันได้ลองแล้ว

เพราะถ้าคุณใช้-(void)viewDidLayoutSubviewsมันจะได้ผลแน่นอน แต่วิธีนี้ถูกเรียกทุกครั้งที่ UI ของคุณต้องการการอัปเดต / การเปลี่ยนแปลง ซึ่งจะจัดการได้ยาก ซอฟต์คีย์คือคุณใช้ตัวแปรบูลเพื่อหลีกเลี่ยงการโทรวนซ้ำ ใช้ดีกว่าviewWillAppear. โปรดจำไว้ว่าviewWillAppearจะถูกเรียกหากมุมมองถูกโหลดกลับมาอีกครั้ง (โดยไม่ต้องจัดสรรใหม่)


2

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


1
ฉันเห็นเป็นอย่างอื่น ค่าเฟรมจะไม่เปลี่ยนแปลงแม้ว่าจะเปลี่ยนข้อ จำกัด ด้านความกว้าง / ความสูงโดยตรงแล้วก็ตาม (โดยการเปลี่ยนค่าคงที่ฉันมี IBOutlet อยู่ใน xib)
เมื่อ

ปัญหาของฉันไม่เหมือนใครเพราะฉันเปลี่ยนข้อ จำกัด จากนั้นต้องใช้เฟรมในฟังก์ชั่นเดียวกัน การเรียก setNeedsLayout จะไม่อัปเดตทันที ฉันเดาว่าฉันต้องรอให้เค้าโครงก่อนจากนั้นจึงใช้เฟรม คำถามที่สองของฉันคือฉันจะเปลี่ยนข้อ จำกัด ด้านความสูง / ความกว้างได้อย่างไรหากฉันไม่มีข้อมูลอ้างอิงโดยตรง
yuf

1
คุณควร dispatch_async จากนั้นและจะช่วยให้คุณเลื่อนรหัสของคุณไปจนถึงเฟรมถัดไป สำหรับส่วนที่สอง ... ฉันไม่รู้ ... คุณจะต้องทบทวนข้อ จำกัด ทั้งหมดและมองหาว่าข้อใดใช้ได้ ... IBOutlets นั้นง่ายกว่ามาก
borrrden

เป็นจริงสำหรับ IBOutlets แต่ฉันต้องการเขียนฟังก์ชันในหมวดหมู่สำหรับ UIView ที่ฉันไม่มี IB ให้ทำงานด้วย
yuf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.