พยายามทำให้ข้อ จำกัด เคลื่อนไหวอย่างรวดเร็ว


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
ใครสามารถอธิบายได้ว่าทำไม / ทำไมถึงได้ผล? การเรียก animationWithDuration บน UIView ใด ๆ จะทำให้คลาสใด ๆ ที่สืบทอดมาจาก UIView และเปลี่ยนแปลงค่าทางกายภาพหรือไม่
ชาวนิปปอน

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

2
@ Jacky บางทีคุณอาจเข้าใจผิดกับเหตุผล Objective-C ในการอ้างอิงถึงตัวแปรที่แข็งแกร่งและอ่อนแอ การปิดสวิฟท์นั้นแตกต่างกัน: เมื่อการปิดเสร็จสิ้นจะไม่มีการถือวัตถุใดself
Mundi

2
ไม่ทำงานในกรณีของฉันมันเกิดขึ้นก่อนสิ่งที่ต้องทำ
Shakti

13
ฉันใช้เวลาหนึ่งชั่วโมงในการสังเกตเห็นว่าฉันต้องโทรเลย์เอาต์ถ้าจำเป็นต้องกำกับดูแล ...
Nominalista

28

SWIFT 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 ล่าช้า: 0 ตัวเลือก: .curveEaseInOut, ภาพเคลื่อนไหว: {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()} เสร็จสิ้น: ไม่มี)
saurabhgoyal795

1
คุณต้องเปลี่ยนข้อ จำกัดแล้วทำให้เคลื่อนไหว
JaredH

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

บรรทัดข้างต้นทำวันของฉัน :) ขอบคุณ @Hadzi
Nrv

18

เป็นสิ่งสำคัญมากที่จะต้องระบุว่าview.layoutIfNeeded()ใช้กับการดูย่อยเท่านั้น

ดังนั้นในการเคลื่อนไหวข้อ จำกัด มุมมองมันเป็นสิ่งสำคัญที่จะเรียกมันในมุมมองต่อการเคลื่อนไหวSuperViewดังนี้

    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 และสิ่งนี้ทำงานได้ในขณะที่เรียกโครงร่างถ้าจำเป็นในมุมมอง แต่ไม่ใช่ superview ไม่ มีประโยชน์จริงๆขอบคุณ!
Alekxos

นี่เป็นคำตอบที่ถูกต้องจริงๆ หากคุณไม่เรียกมันว่าอยู่บน superview มุมมองของคุณจะข้ามไปยังตำแหน่งใหม่ นี่คือความแตกต่างที่สำคัญมาก.
Bryan Deemer

11

ด้วย Swift 5 และ iOS 12.3 ตามความต้องการของคุณคุณสามารถเลือกหนึ่งใน 3 วิธีต่อไปนี้เพื่อแก้ไขปัญหาของคุณ


# 1 ใช้UIViewเป็นanimate(withDuration:animations:)วิธีการเรียน

animate(withDuration:animations:) มีการประกาศดังต่อไปนี้:

การเปลี่ยนแปลงภาพเคลื่อนไหวอย่างน้อยหนึ่งครั้งโดยใช้ระยะเวลาที่กำหนด

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

รหัสสนามเด็กเล่นด้านล่างแสดงให้เห็นถึงการใช้งานที่เป็นไปได้ของ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 ใช้UIViewPropertyAnimator's init(duration:curve:animations:)initialiser และstartAnimation()วิธีการ

init(duration:curve:animations:) มีการประกาศดังต่อไปนี้:

เริ่มต้นแอนิเมชั่นด้วยเส้นโค้งเวลาของ UIKit ในตัว

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

รหัสสนามเด็กเล่นด้านล่างแสดงการใช้งานที่เป็นไปได้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()

# 3 ใช้UIViewPropertyAnimatorเป็นrunningPropertyAnimator(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

รหัสสนามเด็กเล่นด้านล่างแสดงให้เห็นถึงการใช้งานที่เป็นไปได้ของ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.