setNeedsLayout vs. setNeedsUpdateConstraints และ layoutIfNeeded vs updateConstraintsIfNeeded


227

ฉันรู้ว่าห่วงโซ่การจัดวางอัตโนมัติประกอบด้วยกระบวนการที่แตกต่างกัน 3 ประการ

  1. อัปเดตข้อ จำกัด
  2. มุมมองเค้าโครง (นี่คือที่ที่เราได้รับการคำนวณเฟรม)
  3. แสดง

อะไรไม่ได้โดยสิ้นเชิงชัดเจนกับผมคือความแตกต่างระหว่างภายในและ-setNeedsLayout -setNeedsUpdateConstraintsจาก Apple Docs:

setNeedsLayout

เรียกวิธีนี้ในเธรดหลักของแอปพลิเคชันของคุณเมื่อคุณต้องการปรับเค้าโครงของมุมมองย่อย วิธีนี้จะทำการบันทึกคำขอและส่งคืนทันที เนื่องจากวิธีนี้ไม่ได้บังคับให้มีการอัปเดตทันที แต่รอรอบการอัปเดตครั้งต่อไปคุณสามารถใช้วิธีนี้เพื่อทำให้เค้าโครงของมุมมองหลายมุมเป็นโมฆะก่อนที่มุมมองเหล่านั้นจะได้รับการอัปเดต ลักษณะการทำงานนี้ช่วยให้คุณสามารถรวมการปรับปรุงเค้าโครงทั้งหมดของคุณเข้ากับรอบการอัปเดตเดียวซึ่งมักจะดีกว่าสำหรับประสิทธิภาพ

setNeedsUpdateConstraints

เมื่อคุณสมบัติของมุมมองที่กำหนดเองของคุณเปลี่ยนไปในแบบที่จะส่งผลกระทบต่อข้อ จำกัด คุณสามารถเรียกวิธีนี้เพื่อระบุว่าข้อ จำกัด จำเป็นต้องได้รับการปรับปรุงในบางจุดในอนาคต จากนั้นระบบจะเรียกใช้ updateConstraints โดยเป็นส่วนหนึ่งของเค้าโครงผังปกติ การอัปเดตข้อ จำกัด ทั้งหมดในครั้งเดียวก่อนที่จะมีความจำเป็นต้องมั่นใจว่าคุณไม่จำเป็นต้องคำนวณข้อ จำกัด โดยไม่จำเป็นเมื่อมีการเปลี่ยนแปลงหลายอย่างในมุมมองของคุณระหว่างการส่งผ่านเลย์เอาต์

เมื่อฉันต้องการทำให้มุมมองเคลื่อนไหวหลังจากแก้ไขข้อ จำกัด และทำให้การเปลี่ยนแปลงที่ฉันมักจะเรียกเช่น:

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

ฉันพบว่าถ้าฉันใช้-setNeedsLayoutแทน-setNeedsUpdateConstraintsทุกอย่างทำงานตามที่คาดไว้ แต่ถ้าฉันเปลี่ยน-layoutIfNeededด้วย-updateConstraintsIfNeededภาพเคลื่อนไหวจะไม่เกิดขึ้น
ฉันพยายามสรุปเอง:

  • -updateConstraintsIfNeeded อัปเดตข้อ จำกัด เท่านั้น แต่ไม่ได้บังคับให้เค้าโครงเข้าสู่กระบวนการดังนั้นเฟรมดั้งเดิมจึงยังคงอยู่
  • -setNeedsLayout-updateContraintsวิธีการโทรด้วย

ดังนั้นเมื่อไรที่จะใช้หนึ่งแทนอีกอัน? และเกี่ยวกับวิธีการจัดวางฉันต้องโทรหาพวกเขาในมุมมองที่มีการเปลี่ยนแปลงในข้อ จำกัด หรือในมุมมองหลัก


27
ฉันไม่เข้าใจว่าผู้คนกำลัง downvoting ... จริงๆ ดังนั้นคุณควรทำอะไรบางอย่างเกี่ยวกับเรื่องนี้เช่นถามเหตุผลว่าเป็นข้อบังคับหรือไม่มีจุดหมายเลย
Andrea

7
บางทีพวกเขาเพียงแค่ต้องได้รับตรา Critic (ลงคะแนนเสียงครั้งแรก)
fujianjin6471

1
ผมขอแนะนำให้คุณดูที่นี่ คำตอบคือทางออกของปัญหาจริง ดูวิดีโอนี้ด้วย
ฮันนี่

คำตอบ:


258

ข้อสรุปของคุณถูกต้อง รูปแบบพื้นฐานคือ:

  • setNeedsUpdateConstraintsทำให้แน่ใจว่ามีการโทรในอนาคตที่จะสายupdateConstraintsIfNeededupdateConstraints
  • setNeedsLayoutทำให้แน่ใจว่ามีการโทรในอนาคตที่จะสายlayoutIfNeededlayoutSubviews

เมื่อlayoutSubviewsถูกเรียกมันจะเรียกupdateConstraintsIfNeededด้วยดังนั้นการเรียกมันด้วยตนเองไม่ค่อยจำเป็นในประสบการณ์ของฉัน ที่จริงแล้วฉันไม่เคยเรียกมันมาก่อนยกเว้นเมื่อทำการดีบักเลย์เอาท์

การอัปเดตข้อ จำกัด ในการใช้setNeedsUpdateConstraintsก็ค่อนข้างหายากเช่นกันobjc.io – a ต้องอ่านเกี่ยวกับ autolayouts– พูดว่า :

หากมีการเปลี่ยนแปลงบางอย่างในภายหลังที่ทำให้ข้อ จำกัด ของคุณเป็นโมฆะคุณควรลบข้อ จำกัด ทันทีและเรียกใช้ setNeedsUpdateConstraints ในความเป็นจริงนั้นเป็นกรณีเดียวที่คุณควรเรียกใช้การส่งผ่านข้อ จำกัด การอัปเดต

นอกจากนี้จากประสบการณ์ของฉันฉันไม่เคยมีข้อ จำกัด ที่ไม่ถูกต้องและไม่ได้ตั้งค่าsetNeedsLayoutในบรรทัดถัดไปของรหัสเพราะข้อ จำกัด ใหม่ค่อนข้างจะขอรูปแบบใหม่

กฎของหัวแม่มือคือ:

  • หากคุณจัดการ จำกัด setNeedsLayoutโดยตรงโทร
  • หากคุณเปลี่ยนเงื่อนไขบางอย่าง (เช่น offsets หรือ smth) ซึ่งจะเปลี่ยนข้อ จำกัด ในupdateConstraintsวิธีการแทนที่ของคุณ(วิธีที่แนะนำในการเปลี่ยนข้อ จำกัด btw) การโทรsetNeedsUpdateConstraintsและเวลาส่วนใหญ่setNeedsLayoutหลังจากนั้น
  • หากคุณต้องการใด ๆ layoutIfNeededของการกระทำดังกล่าวจะมีผลทันที-เช่นเมื่อความต้องการของคุณในการเรียนรู้สูงกรอบใหม่หลังจากที่รูปแบบที่ผ่านผนวกกับ

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


@coverback ดังนั้น objc.io พูดว่า "หากมีอะไรเปลี่ยนแปลงในภายหลังที่ทำให้ข้อ จำกัด ของคุณเป็นโมฆะคุณควรลบข้อ จำกัด ออกทันทีและเรียก setNeedsUpdateConstraints ในความเป็นจริงนั่นเป็นกรณีเดียวที่คุณควรจะเรียกใช้การอัปเดตข้อ จำกัด " และในบล็อกแอนิเมชันมันบอกว่าเมื่อฉันลบเพิ่มหรือเปลี่ยนข้อ จำกัด ฉันต้องเรียก setNeedsLayout ความแตกต่างคืออะไร? ฉันรู้สึกโง่จริง ๆ :(
pash3r

3
@ pash3r ความแตกต่างกำลังอัปเดตอย่างต่อเนื่องไม่ถือว่าเป็น "การทำให้เป็นโมฆะ" การตรวจสอบความถูกต้องคือเมื่อไม่มีความเกี่ยวข้องอีกต่อไปเช่นจะต้องแนบกับมุมมองอื่นหรือลบออกอย่างสมบูรณ์ setNeedsLayoutคงเพียงจะวางมุมมองที่ใกล้ชิดหรือไกลออกไปหรือเปลี่ยนขนาดของมันจึงจำเป็นที่จะต้อง
coverback

@coverback setNeedsLayoutทำให้แน่ใจว่าlayoutSubviewsจะถูกเรียกในรอบการอัปเดตครั้งต่อไป แต่นี่อาจจะไม่เกี่ยวข้องกับlayoutIfNeededอะไร
fujianjin6471

2
@coverback หากคุณจัดการข้อ จำกัด โดยตรงlayoutSubviewsจะถูกเรียกโดยอัตโนมัติไม่จำเป็นต้องโทรsetNeedsLayout
fujianjin6471

ใช่การจัดการคุณสมบัติของข้อ จำกัด โดยตรงจะทำให้เกิดlayoutSubviewsขึ้นดังนั้นจึงไม่จำเป็นต้องทำด้วยตนเอง อย่างไรก็ตามlayoutIfNeededคุณต้องโทรติดต่อหากคุณต้องการให้การเปลี่ยนแปลงมีผลทันทีแทนรอบการจัดหน้าถัดไป
Charlie Martin

89

คำตอบโดย coverbackถูกต้องสวย อย่างไรก็ตามฉันต้องการเพิ่มรายละเอียดเพิ่มเติม

ด้านล่างเป็นแผนภาพของวงจร UIView ทั่วไปซึ่งอธิบายพฤติกรรมอื่น ๆ :

ระยะเวลาของ UIView

  1. ฉันพบว่าถ้าฉันใช้-setNeedsLayoutแทน-setNeedsUpdateConstraintsทุกอย่างทำงานตามที่คาดไว้ แต่ถ้าฉันเปลี่ยน-layoutIfNeededด้วย-updateConstraintsIfNeededภาพเคลื่อนไหวจะไม่เกิดขึ้น

updateConstraintsมักจะไม่ทำอะไรเลย มันแค่แก้ไขข้อ จำกัด ที่ไม่ใช้กับมันจนกระทั่งlayoutSubviewsถูกเรียก layoutSubviewsดังนั้นการเคลื่อนไหวไม่ต้องโทรไป

  1. การเรียก setNeedsLayout ยังใช้วิธี -updateContraints

ไม่จำเป็น หากข้อ จำกัด ของคุณยังไม่ได้รับการแก้ไข UIView updateConstraintsจะข้ามเรียกร้องให้ คุณต้องโทรอย่างชัดเจนsetNeedsUpdateConstraintเพื่อแก้ไขข้อ จำกัด ในกระบวนการ

ในการโทรupdateConstraintsคุณจำเป็นต้องทำสิ่งต่อไปนี้:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];

ขอบคุณสิ่งนี้แก้ปัญหาของฉันได้ ฉันมี UIWindow โดยไม่มี UIView หลักที่มีข้อ จำกัด ชั่วคราวที่เพิ่มเข้ามาเมื่อเรียกใช้ LayoutIfNeeded () หน้าแอนิเมชั่น การเพิ่ม wrapper ของ subview ให้กับ UIWindow และการเรียกทั้งสามวิธีเพื่อแก้ไขปัญหาของฉัน
masterwok

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