신속한 구속 조건 애니메이션


242

탭 할 때 너비를 늘리고 싶은 UITextField가 있습니다. 제약 조건을 설정하고 왼쪽의 제약 조건이 오른쪽에서 애니메이션을 적용하려는 것보다 우선 순위가 낮은 지 확인했습니다.

사용하려고하는 코드는 다음과 같습니다.

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

이것은 작동하지만 즉시 발생하는 것으로 보이며 아무런 움직임도없는 것 같습니다. 나는 아무것도 빠뜨리지 않도록 10 초를 설정하려고했지만 같은 결과를 얻었습니다.

nameInputConstraint는 IB에서 클래스에 연결하기 위해 드래그하여 제어하는 ​​제약 조건의 이름입니다.

미리 도와 주셔서 감사합니다!


답변:


666

먼저 제약 조건을 변경 한 다음 업데이트에 애니메이션을 적용해야합니다.

self.nameInputConstraint.constant = 8

스위프트 2

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

스위프트 3, 4, 5

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

2
누군가 이것이 어떻게 / 왜 작동하는지 설명 할 수 있습니까? UIView에서 animationWithDuration을 호출하면 UIView에서 상속되고 실제 값을 변경하는 클래스에 애니메이션이 적용됩니까?
nipponese

3
언급 한 애니메이션 API 는 뷰 및 레이어 의 속성 을 애니메이션하는 데 사용됩니다 . 여기서 레이아웃변화에 애니메이션을 적용해야 합니다 . 그것이 레이아웃 제약 조건의 상수를 변경하는 것이 요구하는 것입니다. 상수를 변경하는 것만으로는 아무것도하지 않습니다.
Mundi

2
@Jacky 아마도 당신은 강하고 약한 변수와 관련하여 Objective-C 추론으로 이것을 착각했을 것입니다. 스위프트 클로저는 다릅니다. 클로저가 완료되면 클로저에 self사용 된 물체가 없습니다 .
Mundi

2
내 경우에는 작동하지 않습니다, 그것은 아주 먼저, 무엇을해야합니까
Shakti

13
내가 superview에 layoutIfNeeded를 호출해야한다는 것을 알기 위해 1 시간이 걸렸습니다 ...
Nominalista

28

스위프트 4.x :

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

완료된 예 :

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

2
작동하지 않습니다.이 UIView.animate (withDuration : 10, delay : 0, 옵션 : .curveEaseInOut, 애니메이션 : {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()}, 완료 : nil)
saurabhgoyal795

1
구속 조건을 변경 한 다음 애니메이션 적용해야합니다.
JaredH

이 코드 스 니펫이 해결책이 될 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 앞으로 독자에게 질문에 대한 답변을 제공하고 있으며 해당 사람들이 코드 제안의 이유를 모를 수도 있습니다.
Narendra Jadhav 2016 년

위의 라인 내 하루를 만들었습니다 :) 감사합니다 @Hadzi
Nrv

18

view.layoutIfNeeded()뷰 서브 뷰에만 적용되는 것을 지적하는 것이 매우 중요 합니다.

따라서 뷰 제약 조건에 애니메이션을 적용하려면 다음과 같이 대 애니메이션 슈퍼 뷰 에서 호출해야 합니다.

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

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

간단한 레이아웃의 예는 다음과 같습니다.

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

Swift 4에서 높이 제약 조건을 업데이트하면 뷰에서 layoutIfNeeded를 호출하는 동안 효과가 있었지만 슈퍼 뷰는 그렇지 않았습니다. 정말 도움, 감사합니다!
Alekxos

이것이 실제로 정답입니다. 슈퍼 뷰에서 호출하지 않으면보기가 새 위치로 이동합니다. 이것은 매우 중요한 차이점입니다.
Bryan Deemer

11

Swift 5 및 iOS 12.3에서는 필요에 따라 문제를 해결하기 위해 다음 3 가지 방법 중 하나를 선택할 수 있습니다.


#1. 사용 UIViewanimate(withDuration:animations:)클래스 메소드

animate(withDuration:animations:) 다음과 같은 선언이 있습니다.

지정된 지속 시간을 사용하여 하나 이상의 뷰로 애니메이션을 변경합니다.

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

아래의 Playground 코드 animate(withDuration:animations:)는 자동 레이아웃 제약 조건의 지속적인 변화를 애니메이션하기 위해 가능한 구현을 보여줍니다 .

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. 사용 UIViewPropertyAnimatorinit(duration:curve:animations:)initialiser 및 startAnimation()방법을

init(duration:curve:animations:) 다음과 같은 선언이 있습니다.

내장 UIKit 타이밍 곡선을 사용하여 애니메이터를 초기화합니다.

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

아래의 Playground 코드 는 자동 레이아웃 제약 조건의 지속적인 변화를 애니메이션 init(duration:curve:animations:)하고 구현 startAnimation()하기 위해 가능한 구현을 보여줍니다 .

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

#삼. 사용 UIViewPropertyAnimatorrunningPropertyAnimator(withDuration:delay:options:animations:completion:)클래스 메소드

runningPropertyAnimator(withDuration:delay:options:animations:completion:) 다음과 같은 선언이 있습니다.

애니메이션을 즉시 실행하기 시작하는 애니메이터 객체를 만들고 반환합니다.

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

아래의 Playground 코드 runningPropertyAnimator(withDuration:delay:options:animations:completion:)는 자동 레이아웃 제약 조건의 지속적인 변화를 애니메이션하기 위해 가능한 구현을 보여줍니다 .

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
정말 좋은 답변입니다!
Bashta

@Imanou 큰 답변, 세부 사항을 주셔서 감사합니다! 옵션에 대한 간단한 설명과 차이점은 무엇입니까? 나에게 정말 도움이 될 것이며 다른 사람들에게 내가 왜 서로를 선택했는지에 대한 문장을 가질 것이라고 확신합니다.
rayepps

3

필자의 경우 사용자 정의보기 만 업데이트했습니다.

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

시청 .

비디오는 self.view.layoutIfNeeded()다음과 같이 추가해야한다고 말합니다 .

UIView.animate(withDuration: 1.0, animations: {
       self.centerX.constant -= 75
       self.view.layoutIfNeeded()
}, completion: nil)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.