Dynamically Moving (Animating) a UITextField

1.3k views Asked by At

I have been trying to animate a UITextField so that its y position increases by 50px. Basically, it moves up by fifty pixels. This is my code:

@IBAction func textField(sender: AnyObject) {
    let x = self.pw.frame.origin.x
    let y = self.pw.frame.origin.y + 100
    UIView.animateWithDuration(0.5, delay: 0, options: nil, animations: {
        self.pw.frame = CGRectMake(x, y, self.pw.frame.size.width, self.pw.frame.size.height)
    }, completion: nil)

It is in a textField's delegate. This code is run when the UITextField is tapped.

This code, sadly, does not do what I want it to. When run, it moves the text field up by 50 pixels but then moves it right back down. It does not finish with it up in what is supposed to be its final position.

2

There are 2 answers

0
hennes On BEST ANSWER

As mentioned in the comments, you should not manually modify frames when you have autolayout constraints installed. Instead you need to change your constraints to reflect the animation's end result.

The following is a minimal working example. It creates a button and a text field and initially positions the text field 58 points below the end of the button. When you tap the button, the constant on the text field's top spacing constraint is decreased from 58 to 8 to move the text field up.

class ViewController: UIViewController {

    var textFieldTopSpacingConstraint: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Create the button
        let button = UIButton.buttonWithType(.System) as! UIButton
        button.setTranslatesAutoresizingMaskIntoConstraints(false)
        button.setTitle("Tap me!", forState: .Normal)
        button.addTarget(self, action: "buttonTapped", forControlEvents: .TouchUpInside)
        view.addSubview(button)

        // Create the text field
        let textField = UITextField()
        textField.placeholder = "Enter text here"
        textField.setTranslatesAutoresizingMaskIntoConstraints(false)
        view.addSubview(textField)

        let views: [NSObject: AnyObject] = ["button": button, "textField": textField]

        // Layout the button
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[button]-|", options: nil, metrics: nil, views: views))
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-20-[button(44)]", options: nil, metrics: nil, views: views))

        // Layout the text field and remember its top spacing constraint
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[textField]-|", options: nil, metrics: nil, views: views))
        textFieldTopSpacingConstraint = NSLayoutConstraint(item: textField, attribute: .Top, relatedBy: .Equal,
            toItem: button, attribute: .Bottom, multiplier: 1, constant: 58) // The text field starts 58 points below the end of the button
        view.addConstraint(textFieldTopSpacingConstraint!)
    }

    func buttonTapped() {
        // Change to constant on the top spacing constraint to move the text field up
        UIView.animateWithDuration(0.5) {
            self.textFieldTopSpacingConstraint?.constant = 8 // The text field now starts 8 points below the end of the button
            self.view.layoutIfNeeded()
        }
    }

}

When you have your constraints set up through Interface Builder you can create an outlet for the top spacing constraint in your view controller and use that to modify the text field's top space.

1
ielyamani On

You are expecting the pw frame to move by 100, but instead on the device/simulator it is only 50, that is due to the difference between pixels and points (have a look here). As for the frame going back to its original position, you should check the rest of your code.