[แก้ไข: คำเตือน:การสนทนาที่ตามมาทั้งหมดอาจล้าสมัยหรืออย่างน้อยก็ลดลงอย่างหนักจาก iOS 8 ซึ่งอาจไม่ทำให้เกิดข้อผิดพลาดในการเรียกใช้เค้าโครงในเวลาที่มีการเปลี่ยนมุมมอง]
Autolayout vs. View Transforms
การเล่นอัตโนมัติไม่สามารถเล่นได้ดีนักเมื่อดูการแปลง เหตุผลเท่าที่ฉันสามารถแยกแยะได้ก็คือคุณไม่ควรยุ่งกับกรอบของมุมมองที่มีการแปลง (นอกเหนือจากการแปลงค่าเริ่มต้น) - แต่นั่นคือสิ่งที่ autolayout ทำ วิธีการทำงานอัตโนมัติของการเปิดใช้งานอัตโนมัติคือในlayoutSubviews
รันไทม์นั้นเต็มไปด้วยข้อ จำกัด ทั้งหมดและกำหนดกรอบของมุมมองทั้งหมดตามลำดับ
กล่าวอีกนัยหนึ่งข้อ จำกัด ไม่ใช่เวทมนตร์ พวกเขาเป็นเพียงรายการที่ต้องทำ layoutSubviews
เป็นที่ที่ต้องทำรายการสิ่งที่ต้องทำ และทำได้โดยการตั้งค่าเฟรม
ฉันไม่สามารถช่วยเรื่องนี้เป็นข้อผิดพลาด ถ้าฉันใช้การแปลงนี้กับมุมมอง:
v.transform = CGAffineTransformMakeScale(0.5,0.5);
ฉันคาดว่าจะเห็นมุมมองปรากฏขึ้นพร้อมศูนย์กลางของมันในสถานที่เดียวกับก่อนและครึ่งขนาด แต่ขึ้นอยู่กับข้อ จำกัด ของมันนั่นอาจไม่ใช่สิ่งที่ฉันเห็นเลย
[ที่จริงแล้วมีความประหลาดใจที่สองที่นี่: การใช้การแปลงเป็นมุมมองเรียกรูปแบบทันที ดูเหมือนว่าฉันจะเป็นข้อผิดพลาดอื่น หรืออาจเป็นหัวใจของข้อผิดพลาดแรก สิ่งที่ฉันคาดหวังก็คือสามารถเปลี่ยนแปลงไปอย่างน้อยก็จนกว่าจะถึงเวลาเลย์เอาต์เช่นอุปกรณ์หมุนได้เช่นเดียวกับที่ฉันสามารถหนีด้วยภาพเคลื่อนไหวเฟรมจนกระทั่งถึงเวลาเลย์เอาต์ แต่ในความเป็นจริงเวลาเลย์เอาต์ทันทีซึ่งดูเหมือนผิดปกติ]
โซลูชันที่ 1: ไม่มีข้อ จำกัด
วิธีแก้ปัญหาหนึ่งในปัจจุบันคือถ้าฉันจะใช้การแปลงกึ่งโปร่งใสกับมุมมอง (และไม่เพียงแค่โยกเยกมันชั่วคราวอย่างใด) เพื่อลบข้อ จำกัด ทั้งหมดที่มีผลต่อมัน น่าเสียดายที่นี่เป็นสาเหตุทำให้มุมมองหายไปจากหน้าจอเนื่องจากการเปิดอัตโนมัติยังคงเกิดขึ้นและตอนนี้ไม่มีข้อ จำกัด ใด ๆ ที่จะบอกเราว่าต้องวางมุมมองไว้ที่ใด ดังนั้นนอกเหนือจากการลบข้อ จำกัด ฉันตั้งค่ามุมมองเป็นtranslatesAutoresizingMaskIntoConstraints
ใช่ ตอนนี้มุมมองใช้งานได้แบบเก่าโดยไม่ได้รับผลกระทบจากการชำระอัตโนมัติ (มันเป็นผลกระทบจาก autolayout อย่างเห็นได้ชัด แต่นัย autoresizing จำกัด หน้ากากก่อให้เกิดพฤติกรรมของมันจะเป็นเช่นเดียวกับที่มันเป็นก่อน autolayout.)
โซลูชันที่ 2: ใช้ข้อ จำกัด ที่เหมาะสมเท่านั้น
หากดูเหมือนว่ารุนแรงเล็กน้อยวิธีแก้ไขอื่นคือการกำหนดข้อ จำกัด ให้ทำงานอย่างถูกต้องกับการแปลงที่ตั้งใจไว้ หากมุมมองมีการกำหนดขนาดอย่างหมดจดโดยความกว้างและความสูงคงที่ภายในและวางตำแหน่งไว้ตรงกลางอย่างหมดจดตัวอย่างเช่นการแปลงสเกลของฉันจะทำงานตามที่ฉันคาดไว้ ในรหัสนี้ฉันจะลบข้อ จำกัด ที่มีอยู่ในมุมมองย่อย ( otherView
) และแทนที่ด้วยข้อ จำกัด ทั้งสี่ทำให้มีความกว้างและความสูงคงที่และตรึงไว้ที่กึ่งกลางของมัน หลังจากนั้นการเปลี่ยนสเกลของฉันก็ใช้งานได้:
NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
if (con.firstItem == self.otherView || con.secondItem == self.otherView)
[cons addObject:con];
[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];
ผลที่สุดคือถ้าคุณไม่มีข้อ จำกัด ที่ส่งผลต่อเฟรมของมุมมองการเปลี่ยนอัตโนมัติจะไม่แตะที่เฟรมของมุมมองซึ่งเป็นสิ่งที่คุณทำหลังจากที่มีการเปลี่ยนแปลง
โซลูชันที่ 3: ใช้มุมมองย่อย
ปัญหาของทั้งสองวิธีข้างต้นคือเราเสียประโยชน์จากข้อ จำกัด ในการกำหนดมุมมองของเรา นี่คือทางออกที่แก้ปัญหานั้นได้ เริ่มต้นด้วยมุมมองที่มองไม่เห็นซึ่งมีหน้าที่ทำหน้าที่เป็นเจ้าภาพเพียงอย่างเดียวและใช้ข้อ จำกัด เพื่อวางตำแหน่ง ภายในนั้นให้วางมุมมองที่แท้จริงเป็นมุมมองย่อย ใช้ข้อ จำกัด เพื่อวางตำแหน่งมุมมองย่อยภายในมุมมองโฮสต์ แต่ จำกัด ข้อ จำกัด เหล่านั้นไว้กับข้อ จำกัด ที่จะไม่ต่อสู้เมื่อเราใช้การแปลง
นี่คือภาพประกอบ:
มุมมองสีขาวคือมุมมองโฮสต์ คุณควรจะแกล้งทำเป็นว่ามันโปร่งใสและมองไม่เห็นด้วยเหตุนี้ มุมมองสีแดงคือมุมมองย่อยโดยวางตำแหน่งกึ่งกลางไปยังกึ่งกลางของมุมมองโฮสต์ ตอนนี้เราสามารถปรับขนาดและหมุนมุมมองสีแดงรอบจุดศูนย์กลางได้โดยไม่มีปัญหาและภาพประกอบจริงแสดงให้เห็นว่าเราได้ทำไปแล้ว:
self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);
และในขณะเดียวกันข้อ จำกัด ในมุมมองโฮสต์นั้นเก็บไว้ในตำแหน่งที่ถูกต้องในขณะที่เราหมุนอุปกรณ์
โซลูชันที่ 4: ใช้การแปลงเลเยอร์แทน
แทนที่จะใช้การแปลงมุมมองให้ใช้การแปลงเลเยอร์ซึ่งไม่ทริกเกอร์โครงร่างและไม่ทำให้เกิดข้อขัดแย้งกับข้อ จำกัด ในทันที
ตัวอย่างเช่นภาพเคลื่อนไหวมุมมอง "throb" ที่เรียบง่ายนี้อาจแตกภายใต้การค้นหาอัตโนมัติ:
[UIView animateWithDuration:0.3 delay:0
options:UIViewAnimationOptionAutoreverse
animations:^{
v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
v.transform = CGAffineTransformIdentity;
}];
แม้ว่าในที่สุดแล้วจะไม่มีการเปลี่ยนแปลงขนาดของมุมมองเพียงแค่กำหนดtransform
เค้าโครงของสาเหตุที่จะเกิดขึ้นและข้อ จำกัด สามารถทำให้มุมมองกระโดดไปมา (สิ่งนี้รู้สึกเหมือนเป็นแมลงหรืออะไร) แต่ถ้าเราทำสิ่งเดียวกันกับ Core Animation (โดยใช้ CABasicAnimation และนำแอนิเมชันไปใช้กับเลเยอร์ของมุมมอง) เลย์เอาต์ก็ไม่เกิดขึ้นและมันก็ใช้ได้ดี:
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];
layerView
ความกว้างของมันจะรู้ได้อย่างไร? มันเกาะติดด้านขวาของมันกับสิ่งอื่น ๆ หรือไม่?