ฉันจะเปิด / ปิดการใช้งานข้อ จำกัด ของโครงร่างได้เมื่อใด


104

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

ฉันสามารถสลับระหว่างสองชุดโดยทางโปรแกรมได้ดังนี้:

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

แต่ ... ฉันคิดไม่ออกว่าจะทำอย่างนั้นเมื่อไหร่ ดูเหมือนว่าฉันควรจะทำแบบนั้นได้ในครั้งเดียวviewDidLoadแต่ก็ไม่สามารถทำได้ ฉันได้ลองโทรview.updateConstraints()และview.layoutSubviews()หลังจากตั้งค่าข้อ จำกัด แล้ว แต่ก็ไม่มีประโยชน์

ฉันพบว่าถ้าฉันตั้งค่าข้อ จำกัด ในviewDidLayoutSubviewsทุกอย่างทำงานได้ตามที่คาดไว้ ฉันเดาว่าฉันอยากรู้สองสิ่ง ...

  1. ทำไมฉันถึงได้รับพฤติกรรมนี้
  2. เป็นไปได้หรือไม่ที่จะเปิด / ปิดข้อ จำกัด จาก viewDidLoad?

2
คุณหมายถึง deactivateConstraints และ enableConstraints ทำงานใน viewWillLayoutSubviews หรือไม่ ฉันลองแล้ว แต่มันใช้ไม่ได้ที่นั่นหรือใน viewDidLoad มันใช้งานได้ใน viewDidAppear; มุมมองปรากฏขึ้นในที่ที่ควรวางข้อ จำกัด ใหม่ แต่ถ้าฉันหมุนไปที่แนวนอนมุมมองจะย้ายกลับไปยังตำแหน่งที่กำหนดโดยข้อ จำกัด ที่ตั้งไว้ใน IB (และอยู่ที่นั่นเมื่อฉันหมุนกลับเป็นแนวตั้ง) บันทึกข้อ จำกัด แสดงรายการที่ถูกต้อง (ที่เพิ่งเปิดใช้งาน) ดูเหมือนว่าจะเป็นข้อบกพร่องสำหรับฉัน
rdelmar

1
ใช่มันถูกต้อง (ทำงานใน viewDidAppear) และไม่จำเป็นต้องโทรหา super เพราะไม่มีการใช้งาน viewWillLayoutSubviews เริ่มต้น (ฉันลองใช้ด้วยการเรียก super แต่ก็ไม่ได้สร้างความแตกต่าง)
rdelmar

1
@rdelmar เพิ่งมีโอกาสไปทดสอบเพิ่มเติม ... ฉันสามารถตรวจสอบได้ว่าฉันมีพฤติกรรมเดียวกันกับที่คุณอธิบายไว้จริงๆ ... ใช้งานได้ใน viewDidAppear ครั้งแรก แต่จะเปลี่ยนกลับเมื่อหมุน
tybro0103

3
เห็นได้ชัดว่าคุณไม่สามารถทำเครื่องหมายข้อ จำกัด ว่าไม่ได้ติดตั้งใน IB เพื่อจุดประสงค์นี้ พบว่าข้อมูลที่นี่: stackoverflow.com/questions/27663249/…และมันแก้ปัญหาให้ฉันได้
Stefan

1
ฉันมีการใช้ข้อ จำกัด ในลักษณะเดียวกับที่อธิบายไว้ในคำถามยกเว้นว่าฉันเปิดใช้งาน / ปิดใช้งานบางส่วนใน viewDidAppear วิธีนี้ใช้ได้ผล แต่คุณสามารถเห็นองค์ประกอบเปลี่ยนตำแหน่งได้อย่างรวดเร็ว (ปัญหาเล็กน้อย แต่ไม่พึงปรารถนา) การเปลี่ยนแปลง viewWillAppear หรือ viewDidLoad ไม่ได้ผล แต่หลังจากอ่านคำถามนี้ฉันได้ลองทำการเปลี่ยนแปลงใน viewDidLayoutSubviews ใช้งานได้และผู้ใช้ไม่สามารถมองเห็นการเปลี่ยนตำแหน่งได้อีกต่อไป (มันยังทำงานใน viewWillLayoutSubviews) ขอบคุณสำหรับเคล็ดลับนั้น!
peacetype

คำตอบ:


186

ฉันเปิดใช้งานและปิดใช้งานNSLayoutConstraintsในviewDidLoadและผมไม่ได้มีปัญหาใด ๆ กับมัน ดังนั้นจึงได้ผล ต้องมีความแตกต่างในการตั้งค่าระหว่างแอปของคุณกับของฉัน :-)

ฉันจะอธิบายการตั้งค่าของฉัน - อาจทำให้คุณเป็นผู้นำได้:

  1. ฉันตั้งค่า@IBOutletsสำหรับข้อ จำกัด ทั้งหมดที่ฉันต้องเปิดใช้งาน / ปิดใช้งาน
  2. ในการViewControllerบันทึกข้อ จำกัด ลงในคุณสมบัติของคลาสที่ไม่อ่อนแอ เหตุผลนี้คือฉันพบว่าหลังจากปิดการใช้งานข้อ จำกัด ฉันไม่สามารถเปิดใช้งานได้อีกครั้ง - มันเป็นศูนย์ ดังนั้นดูเหมือนว่าจะถูกลบเมื่อปิดใช้งาน
  3. ฉันไม่ได้ใช้NSLayoutConstraint.deactivate/activateเหมือนคุณฉันใช้constraint.active = YES/ NOแทน
  4. หลังจากกำหนดข้อ จำกัด แล้วฉันก็โทรview.layoutIfNeeded().

132
"บันทึกข้อ จำกัด ลงในคุณสมบัติของคลาสที่ไม่อ่อนแอ" คุณช่วยฉันประหยัดเวลาได้มากขอบคุณ!
OpenUserX03

10
"ฉันบันทึกข้อ จำกัด ลงในคุณสมบัติของคลาสที่ไม่อ่อนแอ": สิ่งนี้ช่วยให้ฉันหายปวดใจได้มาก ฉันไม่รู้ว่าฉันกำลังเรียกตัวเลือกบนวัตถุศูนย์ ขอบคุณ !!
static0886

4
สิ่งสำคัญที่ควรทราบว่าข้อ จำกัด "ไม่ใช้งาน" จะไม่ถูกละเลยโดยการจัดวางอัตโนมัติข้อ จำกัด เหล่านี้จะถูกลบออก การเปิดใช้งาน / ปิดใช้งานข้อ จำกัด จะเพิ่มและลบออก ใช้เวลาสักพักในการดีบักโครงร่างอัตโนมัติที่ขัดแย้งกันหลังจากที่ฉันเพิ่มข้อ จำกัด ที่ฉันตั้งไว้ก่อนหน้านี้โดย.active = falseคาดว่าจะถูกละเว้นจนกว่าฉันจะตั้งค่าให้ใช้งานได้
lbarbosa

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

3
Apples doc กล่าวว่า: การเปิดใช้งานหรือปิดใช้งานข้อ จำกัด เรียก addConstraint ( :) และ removeConstraint ( :) ในมุมมองที่เป็นบรรพบุรุษร่วมที่ใกล้เคียงที่สุดของรายการที่จัดการโดยข้อ จำกัด นี้ ใช้คุณสมบัตินี้แทนการเรียก addConstraint ( :) หรือ removeConstraint ( :) โดยตรง ดังนั้นจึงดูเหมือนว่าเมื่อปิดใช้งานข้อ จำกัด จะถูกลบออกและไม่มีการอ้างอิงที่ชัดเจนเกี่ยวกับข้อ จำกัด ที่เหลือเว้นแต่ IBOutlet จะแข็งแกร่ง ดังนั้นข้อ จำกัด จึงถูกลบ IMHO นี่เกือบจะเป็นข้อบกพร่องหรืออย่างน้อยก็เป็นพฤติกรรมที่ไม่คาดคิด
Olle Raab

52

บางทีคุณอาจจะตรวจสอบของคุณ@propertiesแทนที่weakstrongด้วย

บางครั้งอาจเป็นเพราะactive = NOตั้งค่าไว้self.yourConstraint = nilเพื่อให้คุณไม่สามารถใช้งานได้self.yourConstraintอีก


5
ตามที่ระบุไว้ในคู่มือภาษา Swiftคุณสมบัติจะแข็งแกร่งตามค่าเริ่มต้นดังนั้นคุณจึงสามารถลบออกได้weakและจะทำเช่นนั้น
Jonathan Cabrera

30
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

1
เนื่องจากตัวควบคุมมุมมองของฉันเป็นตัวควบคุมมุมมองเด็กการทำใน "didLayoutSubviews" ดูเหมือนจะเป็นวิธีเดียวที่ได้ผล !! FYI.
TalL

นี่เป็นคำตอบเดียวที่ถูกต้อง
Yunus Eren Güzel

@TalL คุณหมายถึงข้อ จำกัด ในตัวควบคุมมุมมองเด็กเองหรือเป็นมุมมองย่อย?
Stefan

นี่คือสิ่งที่ดีที่สุด
ACAkgul

14

ฉันเชื่อว่าปัญหาที่คุณพบเกิดจากข้อ จำกัด ที่ไม่ได้ถูกเพิ่มเข้าไปในมุมมองของพวกเขาจนกว่าviewDidLoad()จะมีการเรียกใช้AFTER คุณมีตัวเลือกมากมาย:

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

B)หากคุณต้องการใช้constraints()ฟังก์ชันของ UIView เพื่อเข้าถึงข้อ จำกัด ต่างๆคุณต้องรอviewDidLayoutSubviews()ให้เริ่มต้นและดำเนินการที่นั่นเนื่องจากเป็นจุดแรกหลังจากสร้างตัวควบคุมมุมมองจากปลายปากกาซึ่งจะมีข้อ จำกัด ที่ติดตั้งไว้ อย่าลืมโทรlayoutIfNeeded()เมื่อคุณทำเสร็จแล้ว สิ่งนี้มีข้อเสียตรงที่การส่งผ่านเค้าโครงจะดำเนินการสองครั้งหากมีการเปลี่ยนแปลงใด ๆ ที่จะนำไปใช้และคุณต้องแน่ใจว่าไม่มีความเป็นไปได้ที่จะเรียกใช้การวนซ้ำแบบไม่สิ้นสุด

คำเตือนสั้น ๆ : ข้อ จำกัด ที่ปิดใช้งานจะไม่ส่งคืนโดย constraints() วิธีการ! ซึ่งหมายความว่าหากคุณปิดการใช้งานข้อ จำกัด โดยตั้งใจที่จะเปิดใช้งานอีกครั้งในภายหลังคุณจะต้องเก็บข้อมูลอ้างอิงไว้

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


11

คุณยังสามารถปรับpriorityคุณสมบัติเป็น "เปิดใช้งาน" และ "ปิดการใช้งาน" ได้ (เช่นค่า 750 เพื่อเปิดใช้งานและ 250 เพื่อปิดใช้งาน) ด้วยเหตุผลบางอย่างการเปลี่ยนactiveBOOL ไม่ได้ส่งผลใด ๆ กับ UI ของฉัน ไม่จำเป็นlayoutIfNeededและสามารถตั้งค่าและเปลี่ยนแปลงได้ที่ viewDidLoad หรือเวลาใดก็ได้หลังจากนั้น


ข้อเสนอแนะที่ดีมาก การเปลี่ยนลำดับความสำคัญของข้อ จำกัด จะทำงานในviewWillTransition(to:, with:)หรือviewWillLayoutSubviews()และคุณสามารถเก็บข้อ จำกัด ทางเลือกทั้งหมดไว้เป็น "ติดตั้ง" ในสตอรีบอร์ด ลำดับความสำคัญข้อ จำกัด 1000ไม่สามารถเปลี่ยนจากการไม่จำเป็นต้องจำเป็นเพื่อให้ค่าการใช้งานดังต่อไปนี้ ในทางกลับกันการเปิดใช้งาน (การเพิ่ม) และการปิดใช้งาน (การลบ) ข้อ จำกัด จะใช้ได้เฉพาะในviewDidLayoutSubviews()และต้องการการstrong @IBOutletอ้างอิงถึงNSLayoutConstraint-s
Gary

"ด้วยเหตุผลบางประการการเปลี่ยน BOOL ที่ใช้งานอยู่ไม่ได้ส่งผลใด ๆ กับ UI ของฉัน" ขึ้นอยู่กับที่นี่ ฉันคิดว่าคุณไม่สามารถเปลี่ยนข้อ จำกัด ที่มีลำดับความสำคัญ 1,000 ในระหว่างรันไทม์ได้ หากคุณต้องการ deactive มันแล้วคุณควรกำหนดลำดับความสำคัญเริ่มต้น 999 หรือลด ....
น้ำผึ้ง

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

อาจทำให้เกิดข้อขัดข้องเช่นเดียวกับ "ไม่รองรับการเปลี่ยนลำดับความสำคัญจากจำเป็นเป็นไม่ได้อยู่ในข้อ จำกัด ที่ติดตั้งไว้ (หรือในทางกลับกัน) คุณส่งผ่านลำดับความสำคัญ 250 และลำดับความสำคัญที่มีอยู่คือ 1,000"
Karthick Ramesh

8

เวลาที่เหมาะสมในการปิดใช้งานข้อ จำกัด ที่ไม่ได้ใช้:

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}

โปรดทราบว่าviewWillLayoutSubviewsสามารถเรียกได้หลายครั้งดังนั้นจึงไม่มีการคำนวณที่หนักหน่วงที่นี่โอเค?

หมายเหตุ: หากคุณต้องการตอบสนองข้อ จำกัด บางอย่างในภายหลังให้จัดเก็บstrongข้อมูลอ้างอิงไว้เสมอ


2
สำหรับฉันวิธีเดียวที่เชื่อถือได้คือปรับข้อ จำกัด ในviewDidLayoutSubviews(). การปรับข้อ จำกัด ในviewWillLayoutSubviews()ไม่ได้ผลในกรณีของฉัน
petrsyn

6

เมื่อมุมมองถูกสร้างขึ้นจะมีการเรียกวิธีวงจรชีวิตต่อไปนี้ตามลำดับ:

  1. loadView
  2. viewDidLoad
  3. viewWillAppear
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews
  6. viewDidAppear

ตอนนี้สำหรับคำถามของคุณ

  1. ทำไมฉันถึงได้รับพฤติกรรมนี้

คำตอบ: เนื่องจากเมื่อคุณพยายามกำหนดข้อ จำกัด ของมุมมองในviewDidLoadมุมมองไม่มีขอบเขตจึงไม่สามารถกำหนดข้อ จำกัด ได้ หลังจากviewDidLayoutSubviewsนั้นขอบเขตของมุมมองจะถูกสรุปเท่านั้น

  1. เป็นไปได้หรือไม่ที่จะเปิด / ปิดข้อ จำกัด จาก viewDidLoad?

คำตอบ: ไม่ใช่เหตุผลที่อธิบายข้างต้น


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

viewDidLoad ควรจะใช้ได้ดีเนื่องจากมีการสร้างและโหลดมุมมอง ... ในความเป็นจริงที่คุณเปิดใช้งานข้อ จำกัด ส่วนใหญ่จะขึ้นอยู่กับประสิทธิภาพ ฉันเดาว่าปัญหาเดิมไม่เกี่ยวข้องกับตำแหน่งที่เปิดใช้งานข้อ จำกัด stackoverflow.com/questions/19387998/…
Gabe

@ABakerSmith ฉันได้แก้ไขคำตอบของฉันให้ชัดเจนยิ่งขึ้น
Sumeet

1

ฉันพบว่าตราบใดที่คุณตั้งค่าข้อ จำกัด ต่อปกติในการแทนที่- (void)updateConstraints(วัตถุประสงค์ c) โดยมีการstrongอ้างอิงสำหรับการเริ่มต้นที่ใช้ข้อ จำกัด ที่ใช้งานและไม่ได้ใช้งาน และที่อื่น ๆ ในรอบการดูจะปิดใช้งานและ / หรือเปิดใช้งานสิ่งที่คุณต้องการจากนั้นการโทรlayoutIfNeededคุณจะไม่มีปัญหา

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

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