ฉันจะเปลี่ยนแปลงข้อ จำกัด การเคลื่อนไหวได้อย่างไร


959

ฉันกำลังอัปเดตแอปเก่าด้วยแอนAdBannerViewและเมื่อไม่มีโฆษณามันสไลด์ปิดหน้าจอ เมื่อมีโฆษณามันจะเลื่อนบนหน้าจอ สิ่งพื้นฐาน

แบบเก่าฉันตั้งกรอบในบล็อกภาพเคลื่อนไหว รูปแบบใหม่ฉันมีIBOutletข้อ จำกัด ในการจัดวางอัตโนมัติซึ่งกำหนดYตำแหน่งในกรณีนี้คือระยะห่างจากด้านล่างของ superview และแก้ไขค่าคงที่:

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = -32;
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = 0;
    }];
    bannerIsVisible = TRUE;
}

และแบนเนอร์เคลื่อนไหวอย่างที่คาดไว้ แต่ไม่มีภาพเคลื่อนไหว


UPDATE:ฉันได้ดูWWDC 12 พูดคุยแนวปฏิบัติที่เป็นเลิศสำหรับเค้าโครงอัตโนมัติ Masteringซึ่งครอบคลุมภาพเคลื่อนไหว มันอธิบายถึงวิธีการปรับปรุงข้อ จำกัด โดยใช้CoreAnimation :

ฉันได้ลองใช้โค้ดต่อไปนี้แล้ว แต่ได้ผลลัพธ์เหมือนกันทุกประการ:

- (void)moveBannerOffScreen {
    _addBannerDistanceFromBottomConstraint.constant = -32;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    _addBannerDistanceFromBottomConstraint.constant = 0;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = TRUE;
}

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


11
ผมไม่เคยเห็นคะแนนโหวตจำนวนมากเพื่อนำเสนอสำหรับคำถามและคำตอบเกี่ยวกับการพิมพ์ผิดในดังนั้นก่อน
abbood

3
หากมีการพิมพ์ผิดในคำตอบคุณควรแก้ไขคำตอบ นั่นเป็นเหตุผลที่พวกเขาสามารถแก้ไขได้
i_am_jorf

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

1
ฉันไม่รู้ว่าพิมพ์ผิดคืออะไร ฉันตอบกลับความคิดเห็นด้านบน
i_am_jorf

9
จากนั้นพิมพ์ผิดเป็นคำถาม อย่างโง่ฉันพิมพ์ "setNeedsLayout" แทน "layoutIfNeeded" มันแสดงให้เห็นอย่างชัดเจนในคำถามของฉันเมื่อฉันตัดและวางรหัสของฉันด้วยข้อผิดพลาดและภาพหน้าจอด้วยคำสั่งที่ถูกต้อง ถึงกระนั้นก็ไม่สามารถสังเกตได้ว่าจะมีคนชี้ให้เห็น
DBD

คำตอบ:


1696

หมายเหตุสำคัญสองประการ:

  1. คุณต้องโทรlayoutIfNeededภายในบล็อคแอนิเมชัน Apple แนะนำให้คุณโทรหาหนึ่งครั้งก่อนที่บล็อกแอนิเมชันเพื่อให้แน่ใจว่าการดำเนินการเลย์เอาต์ที่รอดำเนินการเสร็จสิ้นแล้ว

  2. คุณต้องเรียกใช้เฉพาะในมุมมองพาเรนต์ (เช่นself.view) ไม่ใช่มุมมองชายด์ที่มีข้อ จำกัด ติดอยู่ การทำเช่นนั้นจะอัปเดตมุมมองที่ จำกัดทั้งหมดรวมถึงการสร้างมุมมองอื่น ๆ ที่อาจถูก จำกัด กับมุมมองที่คุณเปลี่ยนข้อ จำกัด ของ (เช่นดู B ติดกับด้านล่างของมุมมอง A และคุณเพิ่งเปลี่ยนมุมมองด้านบนของ A และคุณต้องการเปลี่ยนมุมมอง B จะเคลื่อนไหวด้วย)

ลองสิ่งนี้:

Objective-C

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

สวิฟท์ 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}

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

23
วิธีแก้ปัญหาใช้งานได้ แต่คุณไม่จำเป็นต้องเปลี่ยนค่าคงที่ข้อ จำกัดภายในบล็อกภาพเคลื่อนไหว มันเป็นเรื่องที่ดีมากที่จะตั้งข้อ จำกัด หนึ่งครั้งก่อนที่จะเริ่มแอนิเมชั่น คุณควรแก้ไขคำตอบของคุณ
Ortwin Gentz

74
สิ่งนี้ใช้ไม่ได้สำหรับฉันในตอนแรกและจากนั้นฉันรู้ว่าคุณต้องโทรเลย์เอาต์หากจำเป็นต้องใช้ในมุมมองผู้ปกครองไม่ใช่มุมมองที่มีข้อ จำกัด
Oliver Pearmain

20
ใช้ layoutIfNeeded จะทำให้ภาพย่อยทั้งหมดรีเฟรชไม่ใช่แค่การเปลี่ยนแปลงข้อ จำกัด คุณเคลื่อนไหวข้อ จำกัด ที่เปลี่ยนแปลงได้อย่างไร
ngb

11
"Apple แนะนำให้คุณเรียกมันหนึ่งครั้งก่อนที่บล็อกแอนิเมชันเพื่อให้แน่ใจว่าการดำเนินการเลย์เอาต์ที่รอดำเนินการเสร็จสิ้นแล้ว" ขอบคุณไม่เคยคิดเลย แต่มันสมเหตุสมผล
Rick van der Linde

109

ฉันซาบซึ้งกับคำตอบที่ให้ แต่ฉันคิดว่ามันน่าจะดีกว่า

แอนิเมชันบล็อกพื้นฐานจากเอกสาร

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

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

บล็อกแอนิเมชันที่เรียกใช้เมธอด subviews updateConstraints

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

เมธอด updateConstraints ถูกแทนที่ในคลาสย่อย UIView และต้องเรียกใช้ super เมื่อสิ้นสุดเมธอด

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

AutoLayout Guideเป็นที่ต้องการอย่างมาก แต่ก็คุ้มค่าที่จะอ่าน ตัวฉันเองกำลังใช้สิ่งนี้เป็นส่วนหนึ่งของการUISwitchสลับมุมมองย่อยกับคู่ของUITextFieldเอนิเมชั่นที่เรียบง่ายและบอบบางยุบตัว (ยาว 0.2 วินาที) ข้อ จำกัด สำหรับมุมมองย่อยกำลังถูกจัดการในเมธอดย่อย UIView updateConstraints ตามที่อธิบายไว้ข้างต้น


เมื่อเรียกใช้วิธีการทั้งหมดข้างต้นในself.view(และไม่ได้อยู่ในมุมมองย่อยของมุมมองนั้น) การโทรupdateConstraintsIfNeededไม่จำเป็น (เพราะsetNeedsLayoutจะทริกเกอร์updateConstraintsมุมมองนั้นด้วย) อาจจะน่ารำคาญมากที่สุด แต่มันก็ไม่ได้สำหรับฉันจนถึงขณะนี้;)
anneblue

เป็นการยากที่จะแสดงความคิดเห็นโดยไม่ทราบว่าการทำงานร่วมกันเต็มรูปแบบของ Autolayout และ springs และ struts layout ในกรณีของคุณโดยเฉพาะ ฉันกำลังพูดอย่างสิ้นเชิงเกี่ยวกับสถานการณ์ autolayout บริสุทธิ์ ฉันอยากรู้ว่าสิ่งที่เกิดขึ้นใน layoutSubviews ของคุณเป็นอย่างไร
Cameron Lowell Palmer

translateatesAutoresizingMaskIntoConstraints = NO; หากคุณต้องการที่จะชำระเงินอัตโนมัติโดยบริสุทธิ์ใจคุณควรปิดใช้งานสิ่งนี้อย่างแน่นอน
Cameron Lowell Palmer

ฉันไม่ได้เห็นสิ่งนั้น แต่ฉันไม่สามารถพูดกับปัญหาได้หากไม่มีรหัสบางอย่าง บางทีคุณอาจเยาะเย้ย TableView และติดกับ GitHub?
Cameron Lowell Palmer

ขออภัยฉันไม่ใช้ gitHub :(
anneblue

74

โดยทั่วไปคุณต้องอัปเดตข้อ จำกัด และโทรlayoutIfNeededภายในบล็อกแอนิเมชัน สิ่งนี้อาจเป็นการเปลี่ยน.constantคุณสมบัติของการNSLayoutConstraintเพิ่มการลบข้อ จำกัด (iOS 7) หรือการเปลี่ยน.activeคุณสมบัติของข้อ จำกัด (iOS 8 & 9)

รหัสตัวอย่าง:

[UIView animateWithDuration:0.3 animations:^{
    // Move to right
    self.leadingConstraint.active = false;
    self.trailingConstraint.active = true;

    // Move to bottom
    self.topConstraint.active = false;
    self.bottomConstraint.active = true;

    // Make the animation happen
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

ตัวอย่างการติดตั้ง:

Xcode Project ตัวอย่างโครงการแอนิเมชั่น

การทะเลาะวิวาท

มีคำถามบางอย่างเกี่ยวกับว่าควรเปลี่ยนข้อ จำกัดก่อนบล็อกภาพเคลื่อนไหวหรือข้างใน (ดูคำตอบก่อนหน้า)

ต่อไปนี้เป็นบทสนทนาทวิตเตอร์ระหว่าง Martin Pilkington ผู้สอน iOS และ Ken Ferry ผู้เขียนเค้าโครงอัตโนมัติ เคนอธิบายว่าแม้ว่าการเปลี่ยนค่าคงที่นอกบล็อกภาพเคลื่อนไหวอาจใช้งานได้ในปัจจุบันแต่ก็ไม่ปลอดภัยและควรเปลี่ยนภายในบล็อกภาพเคลื่อนไหว https://twitter.com/kongtomorrow/status/440627401018466305

ภาพเคลื่อนไหว:

ตัวอย่างโครงการ

นี่เป็นโครงการง่าย ๆ ที่แสดงว่ามุมมองสามารถเคลื่อนไหว มันใช้ Objective C และทำให้มุมมองเคลื่อนไหวโดยการเปลี่ยน.activeคุณสมบัติของข้อ จำกัด หลายประการ https://github.com/shepting/SampleAutoLayoutAnimation


+1 สำหรับการแสดงตัวอย่างโดยใช้การตั้งค่าสถานะที่ใช้งานใหม่ นอกจากนี้การเปลี่ยนข้อ จำกัด นอกบล็อกแอนิเมชันจะรู้สึกเหมือนเป็นแฮ็คให้ฉันเสมอ
Korey Hinton

ฉันยกปัญหาขึ้นใน GitHub เพราะวิธีนี้ใช้ไม่ได้กับการย้ายมุมมองกลับไปที่เดิม ฉันเชื่อว่าการเปลี่ยนการใช้งานไม่ใช่โซลูชันที่ถูกต้องและคุณควรเปลี่ยนลำดับความสำคัญแทน ตั้งค่าด้านบนเป็น 750 และด้านล่างเป็น 250 จากนั้นสลับรหัสระหว่าง UILayoutPriorityDefaultHigh และ UILayoutPriorityDefaultLow
59

อีกเหตุผลหนึ่งสำหรับการอัพเดตภายในบล็อกคือมันแยกการเปลี่ยนแปลงจากรหัสการโทรที่อาจเกิดขึ้นกับการเรียกเลย์เอาต์ถ้าจำเป็น (และขอบคุณสำหรับลิงค์ Twitter)
Chris Conover

1
คำตอบที่ดี พิเศษเพราะคุณพูดถึงบทสนทนาของมาร์ตินและเคนเกี่ยวกับเรื่องนี้
โจนา

ฉันสับสนเกี่ยวกับวิธีการปรับปรุงข้อ จำกัด และเขียน นี้ คุณช่วยดูหน่อยได้ไหม?
ฮันนี่

36
// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)

// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{

    // Step 3, call layoutIfNeeded on your animated view's parent
    [self.view layoutIfNeeded];
}];

29

โซลูชัน Swift 4

UIView.animate

สามขั้นตอนง่าย ๆ :

  1. เปลี่ยนข้อ จำกัด เช่น:

    heightAnchor.constant = 50
  2. บอกสิ่งที่บรรจุviewว่าเลย์เอาต์นั้นสกปรกและการกำหนดค่าอัตโนมัติควรคำนวณเค้าโครงใหม่:

    self.view.setNeedsLayout()
  3. ในบล็อกแอนิเมชันบอกเค้าโครงเพื่อคำนวณโครงร่างใหม่ซึ่งเทียบเท่ากับการตั้งค่าเฟรมโดยตรง (ในกรณีนี้การกำหนดค่าอัตโนมัติจะตั้งค่าเฟรม):

    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }

ทำตัวอย่างที่ง่ายที่สุดให้สมบูรณ์:

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

sidenote

มีขั้นตอนที่เป็นทางเลือก 0 - ก่อนที่จะเปลี่ยนข้อ จำกัด ที่คุณอาจต้องการเรียกself.view.layoutIfNeeded()เพื่อให้แน่ใจว่าจุดเริ่มต้นของภาพเคลื่อนไหวนั้นมาจากสถานะที่มีข้อ จำกัด เก่านำไปใช้ (ในกรณีที่มีการเปลี่ยนแปลงข้อ จำกัด อื่น ๆ ที่ไม่ควรรวมไว้ในภาพเคลื่อนไหว ):

otherConstraint.constant = 30
// this will make sure that otherConstraint won't be animated but will take effect immediately
self.view.layoutIfNeeded()

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

UIViewPropertyAnimator

ตั้งแต่ iOS 10 เรามีกลไกแอนิเมชั่นใหม่ - UIViewPropertyAnimatorเราควรทราบว่าโดยทั่วไปแล้วกลไกเดียวกันนี้จะใช้กับมัน ขั้นตอนโดยพื้นฐานแล้วจะเหมือนกัน:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

เนื่องจากanimatorเป็น encapsulation ของแอนิเมชันเราจึงสามารถอ้างอิงและอ้างอิงในภายหลัง อย่างไรก็ตามเนื่องจากในบล็อกนิเมชั่นที่เราเพียงแค่บอก autolayout เพื่อคำนวณเฟรมที่เรามีการเปลี่ยนแปลงข้อ จำกัด startAnimationก่อนที่จะเรียก ดังนั้นสิ่งนี้เป็นไปได้:

// prepare the animator first and keep a reference to it
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}

// at some other point in time we change the constraints and call the animator
heightAnchor.constant = 50
self.view.setNeedsLayout()
animator.startAnimation()

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

นอกจากนี้โปรดจำไว้ว่าตัวสร้างภาพเคลื่อนไหวเดียวไม่สามารถใช้ซ้ำได้ - เมื่อคุณเรียกใช้งานแล้วคุณจะไม่สามารถ "เรียกใช้ซ้ำ" ได้ ดังนั้นฉันคิดว่ามันไม่มีเหตุผลที่ดีที่จะทำให้การ์ตูนเคลื่อนไหวเว้นเสียแต่ว่าเราจะใช้มันเพื่อควบคุมภาพเคลื่อนไหวแบบโต้ตอบ


👍ทำงานได้ดีใน swift 5 ด้วย
spnkr

layoutIfNeeded () คือกุญแจสำคัญ
Medhi

15

สตอรีบอร์ด, รหัส, เคล็ดลับและ Gotchas ไม่กี่

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

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

lazy var centerYInflection:NSLayoutConstraint = {
       let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
        return temp!
}()

หลังจากการทดลองบางอย่างฉันสังเกตเห็นว่าหนึ่งต้องได้รับข้อ จำกัด จากมุมมองข้างต้น (aka กำกับดูแล) ทั้งสองมุมมองที่มีการกำหนดข้อ จำกัด ในตัวอย่างด้านล่าง (ทั้ง MNGStarRating และ UIWebView เป็นไอเท็มสองประเภทที่ฉันสร้างข้อ จำกัด ระหว่างและพวกมันคือ subviews ภายใน self.view)

ตัวกรองเชน

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

ข้อ จำกัด การเคลื่อนไหวโดยใช้ Swift

Nota Bene - ตัวอย่างนี้เป็นสตอรีบอร์ด / การแก้ปัญหารหัสและสมมติว่ามีการกำหนดค่าเริ่มต้นไว้ในกระดานเรื่องราว หนึ่งสามารถเคลื่อนไหวการเปลี่ยนแปลงโดยใช้รหัส

สมมติว่าคุณสร้างคุณสมบัติเพื่อกรองด้วยเกณฑ์ที่ถูกต้องและไปที่จุดผันเฉพาะสำหรับภาพเคลื่อนไหวของคุณ (แน่นอนว่าคุณสามารถกรองอาร์เรย์และวนลูปได้หากคุณต้องการข้อ จำกัด หลายประการ):

lazy var centerYInflection:NSLayoutConstraint = {
    let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
    return temp!
}()

....

บางครั้งในภายหลัง ...

@IBAction func toggleRatingView (sender:AnyObject){

    let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)

    self.view.layoutIfNeeded()


    //Use any animation you want, I like the bounce in springVelocity...
    UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in

        //I use the frames to determine if the view is on-screen
        if CGRectContainsRect(self.view.frame, self.ratingView.frame) {

            //in frame ~ animate away
            //I play a sound to give the animation some life

            self.centerYInflection.constant = aPointAboveScene
            self.centerYInflection.priority = UILayoutPriority(950)

        } else {

            //I play a different sound just to keep the user engaged
            //out of frame ~ animate into scene
            self.centerYInflection.constant = 0
            self.centerYInflection.priority = UILayoutPriority(950)
            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()
         }) { (success) -> Void in

            //do something else

        }
    }
}

ความผิดมากมาย

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

  1. ระวัง zPositioning บางครั้งเมื่อไม่มีอะไรเกิดขึ้นคุณควรซ่อนมุมมองอื่น ๆ หรือใช้มุมมองดีบักเกอร์เพื่อค้นหามุมมองภาพเคลื่อนไหวของคุณ ฉันยังพบกรณีที่ผู้ใช้ระบุ Runtime Attribute หายไปใน xml ของกระดานเรื่องราวและนำไปสู่มุมมองภาพเคลื่อนไหวที่ครอบคลุม (ขณะทำงาน)

  2. ใช้เวลาสักครู่เพื่ออ่านเอกสาร (ใหม่และเก่า) ความช่วยเหลือด่วนและส่วนหัว Apple ทำการเปลี่ยนแปลงมากมายเพื่อจัดการข้อ จำกัด AutoLayout ให้ดียิ่งขึ้น (ดูมุมมองสแต็ก) หรืออย่างน้อยautolayout ตำรา โปรดทราบว่าบางครั้งโซลูชันที่ดีที่สุดอยู่ในเอกสาร / วิดีโอรุ่นเก่า

  3. เล่นกับค่าในภาพเคลื่อนไหวและพิจารณาใช้ตัวแปร animateWithDuration อื่น ๆ

  4. อย่า hardcode ค่าเค้าโครงที่เฉพาะเจาะจงเป็นเกณฑ์ในการพิจารณาการเปลี่ยนแปลงค่าคงที่อื่น ๆ แทนใช้ค่าที่ช่วยให้คุณกำหนดตำแหน่งของมุมมอง CGRectContainsRectเป็นตัวอย่างหนึ่ง

  5. หากจำเป็นอย่าลังเลที่จะใช้ระยะขอบเค้าโครงที่เกี่ยวข้องกับมุมมองที่มีส่วนร่วมในคำจำกัดความของข้อ จำกัด let viewMargins = self.webview.layoutMarginsGuide: เป็นตัวอย่าง
  6. ไม่ทำงานคุณไม่ต้องทำมุมมองทั้งหมดที่มีข้อ จำกัด บนกระดานเรื่องราวมีข้อ จำกัด ที่แนบมากับคุณสมบัติ self.viewName.constraints
  7. เปลี่ยนลำดับความสำคัญของคุณสำหรับข้อ จำกัด น้อยกว่า 1,000 ฉันตั้งค่าของฉันเป็น 250 (ต่ำ) หรือ 750 (สูง) บนกระดานเรื่องราว (หากคุณพยายามเปลี่ยนลำดับความสำคัญ 1,000 รายการเป็นรหัสแอปจะหยุดทำงานเนื่องจากจำเป็นต้องใช้ 1,000 รายการ)
  8. พิจารณาไม่พยายามใช้ activConfaints ทันทีและปิดการใช้งานข้อ จำกัด (พวกเขามีที่ของพวกเขา แต่เมื่อเรียนรู้หรือถ้าคุณใช้กระดานเรื่องราวโดยใช้สิ่งเหล่านี้อาจหมายถึงว่าคุณทำมากเกินไป ~ พวกเขามีสถานที่ตามที่เห็นด้านล่าง)
  9. พิจารณาไม่ใช้ addConstraints / removeConstraints เว้นแต่คุณจะเพิ่มข้อ จำกัด ใหม่ในรหัส ฉันพบว่าเกือบทุกครั้งที่ฉันจัดวางมุมมองในกระดานเรื่องราวด้วยข้อ จำกัด ที่ต้องการ (วางหน้าจอการดู) จากนั้นในรหัสฉันเคลื่อนไหวข้อ จำกัด ที่สร้างไว้ก่อนหน้านี้ในกระดานเรื่องราวเพื่อย้ายมุมมองไปรอบ ๆ
  10. ฉันใช้เวลามากในการสร้างข้อ จำกัด กับคลาสและคลาสย่อยของ NSAnchorLayout ใหม่ งานเหล่านี้ใช้ได้ดี แต่ฉันต้องใช้เวลาสักพักกว่าจะรู้ว่าข้อ จำกัด ทั้งหมดที่ฉันต้องการมีอยู่แล้วในกระดานเรื่องราว หากคุณสร้างข้อ จำกัด ในโค้ดแน่นอนที่สุดแล้วใช้วิธีนี้เพื่อรวมข้อ จำกัด ของคุณ:

ตัวอย่างโซลูชันที่รวดเร็วเพื่อหลีกเลี่ยงเมื่อใช้สตอรีบอร์ด

private var _nc:[NSLayoutConstraint] = []
    lazy var newConstraints:[NSLayoutConstraint] = {

        if !(self._nc.isEmpty) {
            return self._nc
        }

        let viewMargins = self.webview.layoutMarginsGuide
        let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)

        let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
        centerY.constant = -1000.0
        centerY.priority = (950)
        let centerX =  self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
        centerX.priority = (950)

        if let buttonConstraints = self.originalRatingViewConstraints?.filter({

            ($0.firstItem is UIButton || $0.secondItem is UIButton )
        }) {
            self._nc.appendContentsOf(buttonConstraints)

        }

        self._nc.append( centerY)
        self._nc.append( centerX)

        self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
        self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))

        return self._nc
    }()

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

NB - ใช้เวลาสักครู่เพื่ออ่านส่วนการเลย์เอาต์อัตโนมัติด้านล่างและคำแนะนำดั้งเดิม มีวิธีใช้เทคนิคเหล่านี้เพื่อเสริม Dynamic Animators ของคุณ

UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in

            //
            if self.starTopInflectionPoint.constant < 0  {
                //-3000
                //offscreen
                self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
                self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)

            } else {

                self.starTopInflectionPoint.constant = -3000
                 self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
            }

        }) { (success) -> Void in

            //do something else
        }

    }

ตัวอย่างจากคำแนะนำ AutoLayout (หมายเหตุตัวอย่างที่สองใช้สำหรับ OS X) BTW - นี่ไม่ได้อยู่ในคู่มือปัจจุบันเท่าที่ฉันเห็นอีกต่อไป เทคนิคที่ต้องการพัฒนาอย่างต่อเนื่อง

การเปลี่ยนแปลงภาพเคลื่อนไหวที่ทำโดยเค้าโครงอัตโนมัติ

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

ในแอป iOS โค้ดของคุณจะมีลักษณะดังนี้:

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

ใน OS X ใช้รหัสต่อไปนี้เมื่อใช้ภาพเคลื่อนไหวที่มีเลเยอร์สำรอง

[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutSubtreeIfNeeded];
}];

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

[[constraint animator] setConstant:42];

สำหรับผู้ที่เรียนรู้ได้ดีสายตาตรวจสอบต้นนี้วิดีโอจากแอปเปิ้ล

ตั้งใจฟังให้ดี

บ่อยครั้งในเอกสารมีบันทึกย่อขนาดเล็กหรือชิ้นส่วนของรหัสที่นำไปสู่ความคิดที่ใหญ่กว่า ตัวอย่างเช่นการแนบข้อ จำกัด เลย์เอาต์อัตโนมัติกับแอนิเมชั่นแบบไดนามิกเป็นแนวคิดใหญ่

ขอให้โชคดีและบังคับให้อยู่กับคุณ



6

วิธีการทำงาน 100% Swift 3.1

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

self.view.layoutIfNeeded() // Force lays of all subviews on root view
UIView.animate(withDuration: 0.5) { [weak self] in // allowing to ARC to deallocate it properly
       self?.tbConstraint.constant = 158 // my constraint constant change
       self?.view.layoutIfNeeded() // Force lays of all subviews on root view again.
}

4

ฉันพยายามทำให้ข้อ จำกัด มีชีวิตชีวาและไม่ง่ายที่จะหาคำอธิบายที่ดี

อะไรคำตอบอื่น ๆ จะบอกว่าเป็นความจริงโดยสิ้นเชิง: คุณต้องโทรภายใน[self.view layoutIfNeeded]; animateWithDuration: animations:อย่างไรก็ตามจุดสำคัญอื่น ๆ คือการมีพอยน์เตอร์สำหรับทุกNSLayoutConstraintสิ่งที่คุณต้องการทำให้เคลื่อนไหว

ฉันสร้างตัวอย่างใน GitHub


4

การทำงานและเพิ่งทดสอบโซลูชันสำหรับ Swift 3 กับ Xcode 8.3.3:

self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0

UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

เพียงจำไว้ว่า self.calendarViewHeight เป็นข้อ จำกัด ที่อ้างถึง customView (CalendarView) ฉันเรียกว่า .layoutIfNeeded () ที่ self.view และไม่ใช่ใน self.calendarView

หวังว่าความช่วยเหลือนี้


3

มีบทความพูดคุยเกี่ยวกับเรื่องนี้: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was

ซึ่งเขาเขียนดังนี้:

- (void)handleTapFrom:(UIGestureRecognizer *)gesture {
    if (_isVisible) {
        _isVisible = NO;
        self.topConstraint.constant = -44.;    // 1
        [self.navbar setNeedsUpdateConstraints];  // 2
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded]; // 3
        }];
    } else {
        _isVisible = YES;
        self.topConstraint.constant = 0.;
        [self.navbar setNeedsUpdateConstraints];
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded];
        }];
    }
}

หวังว่ามันจะช่วย


1

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

ข้อ จำกัด กำหนดพื้นที่บนสุดจากฟิลด์ข้อความถึงด้านบนของคอนเทนเนอร์ เมื่อเปิดคีย์บอร์ดฉันแค่หารค่าคงที่ 2

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

มันเกิดขึ้นกับฉันอาจมีการจัดวางเพิ่มเติมเกิดขึ้นจากการเปิดคีย์บอร์ด การเพิ่มบล็อก dispatch_after อย่างง่ายด้วยความล่าช้า 10ms ทำให้แอนิเมชั่นทำงานทุกครั้ง - ไม่ต้องกระโดด

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