cố gắng làm sống động một sự hạn chế trong nhanh chóng


242

Tôi có một UITextField mà tôi muốn phóng to chiều rộng của nó khi gõ vào. Tôi thiết lập các ràng buộc và đảm bảo ràng buộc bên trái có mức ưu tiên thấp hơn mức ràng buộc mà tôi đang cố gắng tạo hiệu ứng ở phía bên phải.

Đây là mã mà tôi đang cố gắng sử dụng.

  // move the input box
    UIView.animateWithDuration(10.5, animations: {
        self.nameInputConstraint.constant = 8
        }, completion: {
            (value: Bool) in
            println(">>> move const")
    })

Điều này hoạt động, nhưng nó dường như chỉ xảy ra ngay lập tức và dường như không có bất kỳ chuyển động. Tôi đã cố gắng đặt nó 10 giây để đảm bảo rằng tôi không thiếu thứ gì, nhưng tôi đã nhận được kết quả tương tự.

nameInputConstraint là tên của ràng buộc mà tôi kiểm soát được kéo để kết nối với lớp của tôi từ IB.

Cảm ơn vì sự giúp đỡ của bạn trước đó!


Câu trả lời:


665

Trước tiên bạn cần thay đổi các ràng buộc và sau đó làm động bản cập nhật.

self.nameInputConstraint.constant = 8

Swift 2

UIView.animateWithDuration(0.5) {
    self.view.layoutIfNeeded()
}

Swift 3, 4, 5

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

2
Ai đó có thể giải thích làm thế nào / tại sao điều này làm việc? Gọi hoạt hìnhWithDuration trên bất kỳ UIView nào sẽ làm động bất kỳ lớp nào kế thừa từ UIView và thay đổi giá trị vật lý?
nipponese

3
API hoạt hình mà bạn đề cập được sử dụng để làm động các thuộc tính của chế độ xem và lớp. Ở đây chúng ta cần làm sinh động sự thay đổi trong cách bố trí . Đó là những gì thay đổi hằng số của một ràng buộc bố cục đòi hỏi - thay đổi hằng số không làm gì cả.
Mundi

2
@Jacky Có lẽ bạn đang nhầm lẫn điều này với lý luận Objective-C liên quan đến các biến mạnh và yếu. Đóng cửa Swift là khác nhau: một khi đóng cửa kết thúc, không có đối tượng giữ cho selfsử dụng trong đóng cửa.
Mundi

2
không hoạt động trong trường hợp của tôi, nó xảy ra đầu tiên, phải làm gì
Shakti

13
Tôi phải mất một giờ để thông báo rằng tôi phải gọi bố trí Nếu cần trên giám sát ...
Nominalista

28

Chuyển 4.x:

self.mConstraint.constant = 100.0
UIView.animate(withDuration: 0.3) {
        self.view.layoutIfNeeded()
}

Ví dụ hoàn thành:

self.mConstraint.constant = 100
UIView.animate(withDuration: 0.3, animations: {
        self.view.layoutIfNeeded()
    }, completion: {res in
        //Do something
})

2
nó không làm việc tôi đã làm điều này UIView.animate (withDuration: 10, chậm trễ: 0, tùy chọn: .curveEaseInOut, hình động: {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()}, hoàn thành: nil)
saurabhgoyal795

1
Bạn phải thay đổi các ràng buộc và sau đó hoạt hình.
JaredH

Mặc dù đoạn mã này có thể là giải pháp, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Narendra Jadhav

Dòng trên Tạo nên ngày của tôi :) Cảm ơn bạn @Hadzi
Nrv

18

Điều rất quan trọng là chỉ ra rằng chỉ view.layoutIfNeeded()áp dụng cho các lượt xem.

Do đó để animate hạn chế tầm nhìn, điều quan trọng là để gọi nó vào xem-to-Animate SuperView như sau:

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

        // request layout on the *superview*
        self.view.superview?.layoutIfNeeded()
    }

Một ví dụ cho cách bố trí đơn giản như sau:

class MyClass {

    /// Container view
    let container = UIView()
        /// View attached to container
        let view = UIView()

    /// Top constraint to animate
    var topConstraint = NSLayoutConstraint()


    /// Create the UI hierarchy and constraints
    func createUI() {
        container.addSubview(view)

        // Create the top constraint
        topConstraint = view.topAnchor.constraint(equalTo: container.topAnchor, constant: 0)


        view.translatesAutoresizingMaskIntoConstraints = false

        // Activate constaint(s)
        NSLayoutConstraint.activate([
           topConstraint,
        ])
    }

    /// Update view constraint with animation
    func updateConstraint(heightShift: CGFloat) {
        topConstraint.constant = heightShift

        UIView.animate(withDuration: 0.3) {

            // request layout on the *superview*
            self.view.superview?.layoutIfNeeded()
        }
    }
}

Cập nhật một hạn chế về chiều cao trên Swift 4 và điều này đã hoạt động trong khi gọi layout IfNeeded trên chế độ xem nhưng không phải giám sát thì không. Thực sự hữu ích, cảm ơn!
Alekxos

Đây thực sự là câu trả lời chính xác. Nếu bạn không gọi nó trên giám sát, chế độ xem của bạn sẽ nhảy đến vị trí mới. Đây là một sự phân biệt rất quan trọng.
Bryan Deemer

11

Với Swift 5 và iOS 12.3, theo nhu cầu của bạn, bạn có thể chọn một trong 3 cách sau để giải quyết vấn đề của mình.


# 1. Sử dụng UIViewanimate(withDuration:animations:)phương pháp lớp

animate(withDuration:animations:) có tuyên bố sau:

Animate thay đổi thành một hoặc nhiều chế độ xem bằng thời lượng được chỉ định.

class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void)

Mã Playground bên dưới cho thấy khả năng triển khai có thể animate(withDuration:animations:)để làm động sự thay đổi liên tục của ràng buộc Bố cục tự động.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIView.animate(withDuration: 2) {
            self.view.layoutIfNeeded()
        }
    }

}

PlaygroundPage.current.liveView = ViewController()

# 2. Sử dụng UIViewPropertyAnimator's init(duration:curve:animations:)initialiser và startAnimation()phương pháp

init(duration:curve:animations:) có tuyên bố sau:

Khởi tạo trình hoạt hình với đường cong thời gian UIKit tích hợp.

convenience init(duration: TimeInterval, curve: UIViewAnimationCurve, animations: (() -> Void)? = nil)

Mã sân chơi dưới đây cho thấy khả năng triển khai có thể init(duration:curve:animations:)startAnimation()để tạo hiệu ứng thay đổi liên tục của ràng buộc Bố cục tự động.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: {
            self.view.layoutIfNeeded()
        })
        animator.startAnimation()
    }

}

PlaygroundPage.current.liveView = ViewController()

# 3. Sử dụng UIViewPropertyAnimatorrunningPropertyAnimator(withDuration:delay:options:animations:completion:)phương pháp lớp

runningPropertyAnimator(withDuration:delay:options:animations:completion:) có tuyên bố sau:

Tạo và trả về một đối tượng hoạt hình bắt đầu chạy hoạt hình của nó ngay lập tức.

class func runningPropertyAnimator(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: @escaping () -> Void, completion: ((UIViewAnimatingPosition) -> Void)? = nil) -> Self

Mã Playground bên dưới cho thấy khả năng triển khai có thể runningPropertyAnimator(withDuration:delay:options:animations:completion:)để làm động sự thay đổi liên tục của ràng buộc Bố cục tự động.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 0, options: [], animations: {
            self.view.layoutIfNeeded()
        })
    }

}

PlaygroundPage.current.liveView = ViewController()

1
Thật là một câu trả lời tuyệt vời!
Bashta

@Imanou câu trả lời tuyệt vời, đánh giá cao các chi tiết! Có thể thêm một mô tả ngắn về các tùy chọn và sự khác biệt là gì? Sẽ thực sự hữu ích với tôi và tôi chắc chắn với người khác sẽ có một câu về lý do tại sao tôi lại chọn cái này hơn cái khác.
rayepps

3

Trong trường hợp của tôi, tôi chỉ cập nhật chế độ xem tùy chỉnh.

// DO NOT LIKE THIS
customView.layoutIfNeeded()    // Change to view.layoutIfNeeded()
UIView.animate(withDuration: 0.5) {
   customViewConstraint.constant = 100.0
   customView.layoutIfNeeded() // Change to view.layoutIfNeeded()
}

-1

Xem này .

Video nói rằng bạn chỉ cần thêm self.view.layoutIfNeeded()như sau:

UIView.animate(withDuration: 1.0, animations: {
       self.centerX.constant -= 75
       self.view.layoutIfNeeded()
}, completion: nil)
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.