Làm thế nào để tôi hoạt hình thay đổi ràng buộc?


959

Tôi đang cập nhật một ứng dụng cũ với một AdBannerViewvà khi không có quảng cáo, nó sẽ tắt màn hình. Khi có một quảng cáo, nó sẽ trượt trên màn hình. Vật liệu cơ bản.

Kiểu cũ, tôi đặt khung hình trong một khối hoạt hình. Kiểu mới, tôi có một IBOutletràng buộc bố trí tự động xác định Yvị trí, trong trường hợp này là khoảng cách từ dưới cùng của giám sát và sửa đổi hằng số:

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

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

Và biểu ngữ di chuyển, chính xác như mong đợi, nhưng không có hình ảnh động.


CẬP NHẬT: Tôi đã xem lại WWDC 12 nói về Thực tiễn tốt nhất để Làm chủ Bố cục tự động bao gồm hoạt hình. Nó thảo luận về cách cập nhật các ràng buộc bằng CoreAnimation :

Tôi đã thử với đoạn mã sau, nhưng nhận được kết quả chính xác như sau:

- (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;
}

Bên cạnh đó, tôi đã kiểm tra nhiều lần và điều này đang được thực hiện trên luồng chính .


11
Tôi chưa bao giờ thấy rất nhiều phiếu bầu cho một câu hỏi và câu trả lời về lỗi đánh máy trên SO trước đây
abbood

3
Nếu có một lỗi đánh máy trong câu trả lời, bạn nên chỉnh sửa câu trả lời. Đó là lý do tại sao chúng có thể chỉnh sửa.
i_am_jorf

@jeffamaphone - Sẽ hữu ích hơn nếu bạn chỉ ra lỗi đánh máy để tôi biết lỗi ở đâu. Bạn có thể tự chỉnh sửa câu trả lời và sửa lỗi đánh máy cho những người khác của chúng tôi. Tôi đã chỉ chỉnh sửa nó để loại bỏ hằng số khỏi khối hoạt hình, nếu đó là những gì bạn đang đề cập đến.
DBD

1
Tôi không biết lỗi đánh máy là gì. Tôi đã trả lời các ý kiến ​​trên.
i_am_jorf

9
Sau đó, lỗi đánh máy câu hỏi. Ngớ ngẩn tôi đã gõ "setNeedLayout" thay vì "layout IfNeeded". Nó được hiển thị rõ ràng trong câu hỏi của tôi khi tôi cắt và dán mã của mình với lỗi và ảnh chụp màn hình với lệnh chính xác. Nhưng dường như không thể nhận thấy nó cho đến khi ai đó chỉ ra nó.
DBD

Câu trả lời:


1697

Hai lưu ý quan trọng:

  1. Bạn cần gọi layoutIfNeededtrong khối hoạt hình. Apple thực sự khuyên bạn nên gọi nó một lần trước khi khối hoạt hình để đảm bảo rằng tất cả các hoạt động bố trí đang chờ xử lý đã được hoàn thành

  2. Bạn cần gọi nó một cách cụ thể trên khung nhìn cha mẹ (ví dụ self.view), chứ không phải khung nhìn con có các ràng buộc kèm theo nó. Làm như vậy sẽ cập nhật tất cả các chế độ xem bị ràng buộc, bao gồm hoạt hình các chế độ xem khác có thể bị hạn chế đối với chế độ xem mà bạn đã thay đổi ràng buộc (ví dụ: Chế độ xem B được gắn vào dưới cùng của Chế độ xem A và bạn chỉ thay đổi phần bù trên cùng của Chế độ xem A và bạn muốn Xem B để làm động với nó)

Thử cái này:

Mục tiêu-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;
}

Swift 3

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

199
Bạn biết những gì ... câu trả lời của bạn hoạt động. WWDC hoạt động .... tầm nhìn của tôi thất bại. Vì một số lý do, tôi phải mất một tuần để nhận ra mình đang gọi setNeedsLayoutthay layoutIfNeeded. Tôi hơi kinh hoàng bởi bao nhiêu giờ tôi đã dành để không nhận ra tôi chỉ gõ sai tên phương thức.
DBD

23
Giải pháp hoạt động nhưng bạn không cần thay đổi hằng số ràng buộc trong khối hoạt hình. Hoàn toàn ổn khi đặt ràng buộc một lần trước khi khởi động hoạt hình. Bạn nên chỉnh sửa câu trả lời của bạn.
Ortwin Gentz

74
Điều này ban đầu không hiệu quả với tôi và sau đó tôi nhận ra rằng bạn cần gọi layout IfNeeded trên khung nhìn PARENT, chứ không phải chế độ xem áp dụng các ràng buộc.
Oliver Pearmain

20
bằng cách sử dụng layout IfNeeded sẽ làm sinh động tất cả các lần xem lại làm mới không chỉ thay đổi ràng buộc. Làm thế nào để bạn làm động các thay đổi ràng buộc chỉ?
ngb

11
"Apple thực sự khuyên bạn nên gọi nó một lần trước khi khối hoạt hình để đảm bảo rằng tất cả các hoạt động bố trí đang chờ xử lý đã được hoàn thành", cảm ơn bạn, không bao giờ nghĩ về điều đó, nhưng nó có ý nghĩa.
Rick van der Linde

109

Tôi đánh giá cao câu trả lời được cung cấp, nhưng tôi nghĩ sẽ tốt hơn nếu đưa nó đi xa hơn một chút.

Khối hoạt hình cơ bản từ tài liệu

[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
}];

nhưng thực sự đây là một kịch bản rất đơn giản. Điều gì xảy ra nếu tôi muốn làm động các ràng buộc của khung nhìn phụ thông qua updateConstraintsphương thức này?

Một khối hoạt hình gọi phương thức updateConstraint xem xét

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

Phương thức updateConstraint bị ghi đè trong lớp con UIView và phải gọi siêu ở cuối phương thức.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

Hướng dẫn AutoLayout để lại nhiều điều mong muốn nhưng nó đáng để đọc. Bản thân tôi đang sử dụng điều này như là một phần của UISwitchviệc bật tắt một khung nhìn phụ với một cặp UITextFields với hoạt hình thu gọn đơn giản và tinh tế (dài 0,2 giây). Các ràng buộc cho chế độ xem phụ đang được xử lý trong các phương thức updateConstraint của lớp con UIView như mô tả ở trên.


Khi gọi tất cả các phương thức trên self.view(và không phải trên một khung nhìn phụ của chế độ xem đó), việc gọi updateConstraintsIfNeededlà không cần thiết (vì setNeedsLayoutcũng kích hoạt updateConstraintschế độ xem đó). Có thể là tầm thường đối với hầu hết, nhưng nó không dành cho tôi cho đến bây giờ;)
anneblue

Thật khó để bình luận mà không biết sự tương tác đầy đủ của Autolayout và bố trí lò xo và thanh chống trong trường hợp cụ thể của bạn. Tôi đang nói hoàn toàn về một kịch bản tự động thanh toán thuần túy. Tôi sẽ tò mò muốn biết chính xác những gì đang xảy ra trong phương thức layoutSubview của bạn.
Cameron Lowell Palmer

dịchAutoresizingMaskIntoConstraint = NO; Nếu bạn muốn tự động hoàn toàn, bạn chắc chắn nên tắt cái này.
Cameron Lowell Palmer

Tôi đã không nhìn thấy điều đó, nhưng tôi thực sự không thể nói vấn đề mà không có một số mã. Có lẽ bạn có thể giả lập TableView và dán nó trên GitHub?
Cameron Lowell Palmer

Xin lỗi, tôi không sử dụng gitHub :(
anneblue

74

Nói chung, bạn chỉ cần cập nhật các ràng buộc và gọi layoutIfNeededbên trong khối hoạt hình. Điều này có thể là thay đổi thuộc .constanttính của một NSLayoutConstraint, thêm các ràng buộc loại bỏ (iOS 7) hoặc thay đổi thuộc .activetính của các ràng buộc (iOS 8 & 9).

Mã mẫu:

[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];
}];

Thiết lập mẫu:

Dự án Xcode nên dự án hoạt hình mẫu.

Tranh cãi

Có một số câu hỏi về việc nên thay đổi ràng buộc trước khối hoạt hình hay bên trong nó (xem câu trả lời trước).

Sau đây là cuộc trò chuyện trên Twitter giữa Martin Pilkington, người dạy iOS và Ken Ferry, người đã viết Auto Layout. Ken giải thích rằng mặc dù việc thay đổi các hằng số bên ngoài khối hoạt hình hiện có thể hoạt động, nhưng nó không an toàn và chúng thực sự nên được thay đổi bên trong khối hoạt hình. https://twitter.com/kongtomorrow/status/440627401018466305

Hoạt hình:

Dự án mẫu

Đây là một dự án đơn giản cho thấy cách xem có thể được làm động. Nó đang sử dụng Objective C và tạo hiệu ứng cho khung nhìn bằng cách thay đổi thuộc .activetính của một số ràng buộc. https://github.com/shepting/SampleAutoLayoutAnimation


+1 để hiển thị một ví dụ sử dụng cờ hoạt động mới. Ngoài ra, việc thay đổi các ràng buộc bên ngoài khối hoạt hình luôn tạo cảm giác như một vụ hack đối với tôi
Korey Hinton

Tôi đã nêu ra một vấn đề trong github vì giải pháp này không hoạt động để chuyển chế độ xem trở lại vị trí cũ. Tôi tin rằng thay đổi hoạt động không phải là giải pháp chính xác và thay vào đó bạn nên thay đổi mức độ ưu tiên. Đặt đỉnh thành 750 và dưới cùng là 250, sau đó trong mã xen kẽ giữa UILayoutP WarriorityDefaultHigh và UILayoutP WarriorityDefaultLow.
malhal

Một lý do khác để cập nhật bên trong khối là vì nó cô lập các thay đổi từ mã cuộc gọi có thể xảy ra với cách gọi layout IfNeeded. (và cảm ơn vì liên kết Twitter)
Chris Conover

1
Câu trả lời chính xác. Đặc biệt bởi vì bạn đề cập đến cuộc trò chuyện của Martin và Ken về điều này.
Jona

Tôi bối rối về cách cập nhật các ràng buộc và đã viết này . Bạn có thể vui lòng xem qua?
Mật ong

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

Giải pháp Swift 4

UIView.animate

Ba bước đơn giản:

  1. Thay đổi các ràng buộc, ví dụ:

    heightAnchor.constant = 50
  2. Nói với chứa viewrằng bố cục của nó là bẩn và tự động thanh toán sẽ tính toán lại bố cục:

    self.view.setNeedsLayout()
  3. Trong khối hoạt hình, hãy nói bố cục để tính toán lại bố cục, tương đương với việc đặt các khung trực tiếp (trong trường hợp này, tự động thanh toán sẽ đặt các khung):

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

Hoàn thành ví dụ đơn giản nhất:

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

Sidenote

Có một bước 0 tùy chọn - trước khi thay đổi các ràng buộc bạn có thể muốn gọi self.view.layoutIfNeeded()để đảm bảo rằng điểm bắt đầu cho hoạt hình là từ trạng thái với các ràng buộc cũ được áp dụng (trong trường hợp có một số thay đổi ràng buộc khác không được đưa vào hoạt hình ):

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

Vì với iOS 10, chúng tôi đã có một cơ chế hoạt hình mới - UIViewPropertyAnimator, chúng tôi nên biết rằng về cơ bản cùng một cơ chế áp dụng cho nó. Các bước cơ bản giống nhau:

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

animatorlà sự đóng gói của hình ảnh động, chúng ta có thể tiếp tục tham chiếu đến nó và gọi nó sau. Tuy nhiên, vì trong khối hoạt hình, chúng tôi chỉ yêu cầu tự động trả lời để tính toán lại các khung, chúng tôi phải thay đổi các ràng buộc trước khi gọi startAnimation. Vì vậy, một cái gì đó như thế này là có thể:

// 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()

Thứ tự thay đổi các ràng buộc và bắt đầu một trình hoạt hình rất quan trọng - nếu chúng ta chỉ thay đổi các ràng buộc đó và để lại hoạt hình của chúng ta trong một thời gian sau, chu trình vẽ lại tiếp theo có thể gọi phép tính toán lại tự động và thay đổi sẽ không được hoạt hình.

Ngoài ra, hãy nhớ rằng một trình hoạt hình duy nhất không thể tái sử dụng - một khi bạn chạy nó, bạn không thể "chạy lại" nó. Vì vậy, tôi đoán rằng thực sự không có lý do chính đáng để giữ người làm phim hoạt hình, trừ khi chúng ta sử dụng nó để điều khiển hoạt hình tương tác.


👍works tuyệt vời trong swift 5 quá
spnkr

layout IfNeeded () là chìa khóa
Medhi

15

Storyboard, Code, Tips và một vài Gotchas

Các câu trả lời khác chỉ tốt nhưng câu trả lời này nêu bật một vài vấn đề khá quan trọng về các ràng buộc hoạt hình bằng một ví dụ gần đây. Tôi đã trải qua rất nhiều biến thể trước khi tôi nhận ra những điều sau đây:

Tạo các ràng buộc bạn muốn nhắm mục tiêu vào các biến Class để giữ tham chiếu mạnh. Trong Swift tôi đã sử dụng các biến lười biếng:

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!
}()

Sau một số thử nghiệm, tôi đã lưu ý rằng một PHẢI có được các ràng buộc từ chế độ xem TRÊN (hay còn gọi là giám sát) hai chế độ xem các ràng buộc được xác định. Trong ví dụ dưới đây (cả MNGStarRating và UIWebView là hai loại mục tôi đang tạo ra một ràng buộc giữa và chúng là các cuộc phỏng vấn trong self.view).

Lọc chuỗi

Tôi tận dụng phương pháp lọc của Swift để phân tách ràng buộc mong muốn sẽ đóng vai trò là điểm uốn. Người ta cũng có thể trở nên phức tạp hơn nhiều nhưng bộ lọc thực hiện công việc tốt ở đây.

Hạn chế hoạt hình bằng Swift

Nota Bene - Ví dụ này là giải pháp bảng phân cảnh / mã và giả sử người ta đã thực hiện các ràng buộc mặc định trong bảng phân cảnh. Sau đó, người ta có thể làm động các thay đổi bằng cách sử dụng mã.

Giả sử bạn tạo một thuộc tính để lọc với các tiêu chí chính xác và đến một điểm uốn cụ thể cho hoạt hình của bạn (tất nhiên bạn cũng có thể lọc một mảng và lặp qua nếu bạn cần nhiều ràng buộc):

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!
}()

....

Thỉnh thoảng ...

@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

        }
    }
}

Nhiều lượt sai

Những ghi chú này thực sự là một tập hợp các mẹo mà tôi đã viết cho chính mình. Tôi đã làm tất cả các cá nhân và đau đớn. Hy vọng hướng dẫn này có thể tha cho người khác.

  1. Xem ra cho zP vị trí. Đôi khi, khi không có gì rõ ràng xảy ra, bạn nên ẩn một số chế độ xem khác hoặc sử dụng trình gỡ lỗi chế độ xem để xác định vị trí xem hoạt hình của mình. Tôi thậm chí đã tìm thấy các trường hợp thuộc tính Thời gian chạy do Người dùng Xác định bị mất trong xml của bảng phân cảnh và dẫn đến chế độ xem hoạt hình được che (trong khi làm việc).

  2. Luôn dành một phút để đọc tài liệu (mới và cũ), Trợ giúp nhanh và tiêu đề. Apple tiếp tục thực hiện nhiều thay đổi để quản lý tốt hơn các ràng buộc AutoLayout (xem chế độ xem ngăn xếp). Hoặc ít nhất là AutoLayout Cookbook . Hãy nhớ rằng đôi khi các giải pháp tốt nhất là trong các tài liệu / video cũ hơn.

  3. Chơi xung quanh với các giá trị trong hình động và xem xét sử dụng các biến thể animateWithDuration khác.

  4. Không mã hóa các giá trị bố cục cụ thể làm tiêu chí để xác định thay đổi cho các hằng số khác, thay vào đó sử dụng các giá trị cho phép bạn xác định vị trí của chế độ xem. CGRectContainsRectlà một ví dụ

  5. Nếu cần, đừng ngần ngại sử dụng lề bố cục được liên kết với chế độ xem tham gia định nghĩa ràng buộc let viewMargins = self.webview.layoutMarginsGuide: là ví dụ
  6. Đừng làm công việc mà bạn không phải làm, tất cả các chế độ xem có ràng buộc trên bảng phân cảnh đều có các ràng buộc được gắn vào thuộc tính self.viewName.constraint
  7. Thay đổi mức độ ưu tiên của bạn cho bất kỳ ràng buộc nào dưới 1000. Tôi đặt mức của tôi thành 250 (thấp) hoặc 750 (cao) trên bảng phân cảnh; (nếu bạn cố gắng thay đổi mức ưu tiên 1000 thành bất kỳ thứ gì trong mã thì ứng dụng sẽ sập vì 1000 là bắt buộc)
  8. Cân nhắc không ngay lập tức cố gắng sử dụng activConstraint và hủy kích hoạtConstraint (họ có vị trí của họ nhưng khi vừa học hoặc nếu bạn đang sử dụng bảng phân cảnh bằng cách sử dụng những thứ này có thể có nghĩa là bạn làm quá nhiều ~ họ có một vị trí như được thấy bên dưới)
  9. Cân nhắc không sử dụng addConstraint / removeConstraint trừ khi bạn thực sự thêm một ràng buộc mới trong mã. Tôi thấy rằng hầu hết các lần tôi bố trí các khung nhìn trong bảng phân cảnh với các ràng buộc mong muốn (đặt màn hình ngoài màn hình), sau đó bằng mã, tôi làm động các ràng buộc được tạo trước đó trong bảng phân cảnh để di chuyển khung nhìn xung quanh.
  10. Tôi đã dành rất nhiều thời gian lãng phí để xây dựng các ràng buộc với lớp và lớp con NSAnchorLayout mới. Chúng chỉ hoạt động tốt nhưng tôi phải mất một thời gian để nhận ra rằng tất cả các ràng buộc mà tôi cần đã tồn tại trong bảng phân cảnh. Nếu bạn xây dựng các ràng buộc trong mã thì chắc chắn sử dụng phương pháp này để tổng hợp các ràng buộc của bạn:

Mẫu giải pháp nhanh để TRÁNH khi sử dụng Bảng phân cảnh

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
    }()

Nếu bạn quên một trong những mẹo này hoặc những mẹo đơn giản hơn như thêm bố cục vào đâu Nếu cần, rất có thể sẽ không có gì xảy ra: Trong trường hợp đó bạn có thể có một giải pháp nướng một nửa như thế này:

NB - Dành một chút thời gian để đọc Phần AutoLayout bên dưới và hướng dẫn ban đầu. Có một cách để sử dụng các kỹ thuật này để bổ sung cho Dynamic Animators của bạn.

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
        }

    }

Đoạn trích từ Hướng dẫn tự động thanh toán (lưu ý đoạn mã thứ hai là để sử dụng OS X). BTW - Điều này không còn trong hướng dẫn hiện tại như tôi có thể thấy. Các kỹ thuật ưa thích tiếp tục phát triển.

Thay đổi hoạt hình được thực hiện bởi bố cục tự động

Nếu bạn cần toàn quyền kiểm soát các thay đổi hoạt hình được thực hiện bởi Bố cục tự động, bạn phải thực hiện các ràng buộc thay đổi theo chương trình. Khái niệm cơ bản là giống nhau cho cả iOS và OS X, nhưng có một vài khác biệt nhỏ.

Trong một ứng dụng iOS, mã của bạn sẽ trông giống như sau:

[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
}];

Trong OS X, sử dụng mã sau đây khi sử dụng hình động dựa trên lớp:

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

Khi bạn không sử dụng hoạt ảnh dựa trên lớp, bạn phải tạo hiệu ứng hằng số bằng cách sử dụng trình hoạt hình của ràng buộc:

[[constraint animator] setConstant:42];

Đối với những người tìm hiểu trực quan tốt hơn hãy xem video đầu tiên này từ Apple .

Chú ý hơn

Thông thường trong tài liệu có những ghi chú nhỏ hoặc những đoạn mã dẫn đến những ý tưởng lớn hơn. Ví dụ, gắn các ràng buộc bố cục tự động cho các hoạt hình động là một ý tưởng lớn.

Chúc may mắn và có thể lực lượng sẽ được với bạn.


11

Giải pháp nhanh chóng:

yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})

7

Giải pháp làm việc 100% Swift 3.1

Tôi đã đọc tất cả các câu trả lời và muốn chia sẻ mã và phân cấp các dòng mà tôi đã sử dụng trong tất cả các ứng dụng của mình để làm động chúng một cách chính xác, Một số giải pháp ở đây không hoạt động, bạn nên kiểm tra chúng trên các thiết bị chậm hơn, ví dụ như iPhone 5 tại thời điểm này.

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

Tôi đã cố gắng làm sống động các ràng buộc và không thực sự dễ dàng để tìm thấy một lời giải thích tốt.

Những câu trả lời khác đang nói là hoàn toàn đúng: bạn cần gọi [self.view layoutIfNeeded];bên trong animateWithDuration: animations:. Tuy nhiên, điểm quan trọng khác là có con trỏ cho mọi NSLayoutConstraintbạn muốn hoạt hình.

Tôi đã tạo một ví dụ trong GitHub .


4

Giải pháp vừa hoạt động và vừa thử nghiệm cho Swift 3 với 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)

Chỉ cần lưu ý rằng self.calWikiViewHeight là một ràng buộc được gọi là customView (CalendarView). Tôi đã gọi .layout IfNeeded () trên self.view và KHÔNG trên self.calWikiView

Hy vọng điều này giúp đỡ.


3

Có một bài viết nói về điều này: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was

Trong đó, anh ta mã hóa như thế này:

- (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];
        }];
    }
}

Hy vọng nó giúp.


1

Trong ngữ cảnh của hoạt hình ràng buộc, tôi muốn đề cập đến một tình huống cụ thể trong đó tôi hoạt hình một ràng buộc ngay lập tức trong một thông báo keyboard_opened.

Ràng buộc xác định một không gian trên cùng từ trường văn bản đến đỉnh của container. Khi mở bàn phím, tôi chỉ cần chia hằng số cho 2.

Tôi không thể đạt được một hình ảnh động ràng buộc trơn tru liên tục trực tiếp trong thông báo bàn phím. Khoảng một nửa số lượt xem sẽ chỉ nhảy đến vị trí mới - mà không cần hoạt hình.

Nó xảy ra với tôi có thể có một số bố trí bổ sung xảy ra do mở bàn phím. Thêm một khối công cụ đơn giản sau khi trì hoãn 10ms làm cho hình ảnh động chạy mọi lúc - không nhảy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.