ดูการเปลี่ยนแปลงเฟรมระหว่าง viewWillAppear: และ viewDidAppear:


85

ฉันได้ค้นพบพฤติกรรมแปลกในใบสมัครของฉันที่เกี่ยวโยงกันIBOutletจะมีกรอบมุมมองที่เกี่ยวโยงกันระหว่างการโทรในการควบคุมมุมมองของฉันไปและviewWillAppear: viewDidAppear:นี่คือรหัสที่เกี่ยวข้องในUIViewControllerคลาสย่อยของฉัน:

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

-(void)viewDidAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

และผลลัพธ์บันทึกผลลัพธ์:

MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>

ซึ่งแสดงให้เห็นอย่างชัดเจนว่ามีการเปลี่ยนกรอบระหว่างสองสาย ฉันต้องการตั้งค่าด้วยมุมมองในviewDidLoadวิธีการนี้ แต่ถ้าเนื้อหาไม่พร้อมให้ฉันเปลี่ยนจนกว่าจะอยู่บนหน้าจอก็ดูเหมือนจะไร้ประโยชน์ จะเกิดอะไรขึ้น?


2
คุณใช้ autolayout หรือไม่? คุณกำลังเพิ่มมุมมองนี้ในตัวสร้างอินเทอร์เฟซหรือโดยทางโปรแกรม?
Andrea

เปิดใช้งานการจัดวางอัตโนมัติและมุมมองนี้สร้างขึ้นใน IB จากสตอรีบอร์ด
Jumhyn

1
ฉันไม่เคยใช้สตอรี่บอร์ด แต่ถูกต้องที่สุด การใช้กรอบการจัดวางอัตโนมัติของมุมมองของคุณจะถูกตั้งค่าเมื่อเอ็นจิ้นการจัดวางอัตโนมัติเริ่มต้นการคำนวณ ลองถามสิ่งเดียวกันทันทีหลังจาก super of - (void) viewDidLayoutSubviews mpethod ของ view controller ของคุณ
Andrea

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

1
viewDidLayoutSubviewsเป็นวิธีที่ถูกต้อง ฉันต้องใส่เนื้อหาทั้งหมดของฉันในมุมมองย่อยเพื่อไม่ให้มีการเรียกวิธีการใหม่เมื่อใดก็ตามที่ฉันเปลี่ยนกรอบของมุมมองหลัก
Jumhyn

คำตอบ:


112

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


6
- (โมฆะ) viewDidLayoutSubviews คือคำตอบสำหรับฉัน! ขอบคุณมาก!
FrizzTheSnail

คำตอบนี้ไม่ถูกต้องจริงๆ ใช่แน่นอนเป็นเวลา (5?) ปีแล้วที่คุณต้องใช้การจ่ายอัตโนมัติ แต่มีหลายสถานการณ์ ( โดยใช้การจัดวางอัตโนมัติ ) ที่คุณต้องพูดเพิ่มบางอย่างบนหน้าจอ "ก่อนที่จะปรากฏต่อผู้ใช้" (ถ้าคุณทำใน viewDidAppear คุณจะได้รับการสั่นไหวหากคุณทำใน viewWillAppear - ตำแหน่งจะไม่ถูกต้อง) คำตอบที่แท้จริงคือการใช้ viewDidLayoutSubviews
Fattie

add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.... คุณจะแสดงมุมมองนั้นหลายครั้ง
Andrea

156

จากเอกสารประกอบ:

viewWillAppear:

แจ้งผู้ควบคุมมุมมองว่ามุมมองกำลังจะถูกเพิ่มไปยังลำดับชั้นของมุมมอง

viewDidAppear:

แจ้งตัวควบคุมมุมมองว่ามีการเพิ่มมุมมองไปยังลำดับชั้นของมุมมอง

ด้วยเหตุนี้จึงยังไม่มีการกำหนดเฟรมของมุมมองย่อยในviewWillAppear:

วิธีการที่เหมาะสมในการแก้ไข UI ของคุณก่อนที่มุมมองจะถูกนำเสนอบนหน้าจอคือ:

viewDidLayoutSubviews

แจ้งผู้ควบคุมมุมมองว่ามุมมองเพิ่งวางมุมมองย่อย


2
ฮึที่น่ารำคาญ แม้แต่ถ้อยคำของเอกสารประกอบก็ทำให้ดูเหมือนว่า viewWillApepar: และ viewDidAppear: ควรเกิดขึ้นต่อกันโดยตรง
Jumhyn

2 เดือนที่รอคำตอบนี้ ... ขอบคุณที่ค้นพบ !! ขอบคุณมาก!
Rafael Ruiz Muñoz

6
ควรสังเกตว่าviewDidLayoutSubviewsจะถูกเรียกหลายครั้งและไม่ได้อยู่ในเฟรมเดียวกันเสมอไป (ฉันคิดว่ามันถูกเรียกด้วย CGRectZero ในการโทรครั้งแรกในบางครั้ง) ได้รับการเรียกสำหรับทุกมุมมองย่อยที่เพิ่มและการเปลี่ยนแปลงอื่น ๆ ในมุมมอง
bauerMusic

ฉันใช้สิ่งนี้มาตลอดทั้งวัน (viewDidLayoutSubviews ถูกเรียกหลายครั้งรวมถึงเมื่อปิดมุมมอง) และเพิ่งพูด ef ด้วยและใส่ตรรกะในคำสั่ง if และสร้างบูลที่ตั้งค่าเป็นจริงหลังจาก รหัสทำงานหนึ่งครั้ง ฉันคิดว่ามันต้องมีวิธีที่สะอาดกว่านี้ แต่เวลาที่จมอยู่กับมันมากเกินไปแล้ว
โซลินอยด์

เราต้องไปให้ถึงที่สุด! viewDidLayoutSubviews แย่มาก seNeedDisplay ไม่ทำงาน
Yaro

9

โทร

self.scrollView.layoutIfNeeded ()

ในviewWillAppearวิธีการของคุณ หลังจากนั้นคุณสามารถเข้าถึงเฟรมได้และจะมีค่าเดียวกับที่คุณพิมพ์viewDidAppear


1
ไม่นี่ไม่ถูกต้อง ตัวอย่าง: คุณมีแถบนำทางบนหน้าจอ (จากตัวควบคุมการนำทางของคุณ) แม้หลังจาก layoutIfNeeded () แล้วความสูงของ navbar จะไม่รวมอยู่ด้วยดังนั้นขนาดเฟรมของคุณจะเปลี่ยนไป
xaphod

นี่เป็นวิธีที่ดีในการบังคับให้คำนวณใหม่ของมิติข้อมูลกรอบมุมมองเลื่อนเพื่อให้มีความสอดคล้องกันตลอดทั้งลำดับชั้นของการเรียกเค้าโครงมุมมองหากกรอบ scrollView เชื่อมโยงกับข้อ จำกัด ภายใน superview
smakus

4

ในกรณีของฉันย้ายวิธีการที่เกี่ยวข้องกับเฟรมทั้งหมดไปที่

override func viewWillLayoutSubviews()

ทำงานได้อย่างสมบูรณ์ (ฉันพยายามแก้ไขข้อ จำกัด จากสตอรี่บอร์ด)

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