ios Swift 中 UITextField/UIView 的抖动动画

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27987048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:22:53  来源:igfitidea点击:

Shake Animation for UITextField/UIView in Swift

iosswiftuitextfield

提问by Joe CRONE Scrivens

I am trying to figure out how to make the text Field shake on button press when the user leaves the text field blank.

我试图弄清楚当用户将文本字段留空时,如何使文本字段在按下按钮时震动。

I currently have the following code working:

我目前有以下代码工作:

if self.subTotalAmountData.text == "" {

    let alertController = UIAlertController(title: "Title", message:
        "What is the Sub-Total!", preferredStyle: UIAlertControllerStyle.Alert)
    alertController.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default,handler: nil))

    self.presentViewController(alertController, animated: true, completion: nil)

} else {

}

But i think it would be much more appealing to just have the text field shake as an alert.

但我认为将文本字段作为警报摇晃会更有吸引力。

I can't find anything to animate the text field.

我找不到任何可以为文本字段设置动画的内容。

Any ideas?

有任何想法吗?

Thanks!

谢谢!

回答by rakeshbs

You can change the durationand repeatCountand tweak it. This is what I use in my code. Varying the fromValueand toValuewill vary the distance moved in the shake.

您可以更改durationrepeatCount并调整它。这是我在我的代码中使用的。改变fromValuetoValue将改变震动中移动的距离。

let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.07
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: viewToShake.center.x - 10, y: viewToShake.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: viewToShake.center.x + 10, y: viewToShake.center.y))

viewToShake.layer.add(animation, forKey: "position")

回答by Surckarter

The following function is used in any view.

以下函数用于任何视图。

extension UIView {
    func shake() {
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.duration = 0.6
        animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0 ]
        layer.add(animation, forKey: "shake")
    }
}

回答by RomOne

EDIT: using CABasicAnimationcause the app to crash if you ever trigger the animation twice in a row. So be sure to use CAKeyframeAnimation. Bug has been fixed, thanks to the comments :)

编辑:CABasicAnimation如果您连续两次触发动画,则使用会导致应用程序崩溃。所以一定要使用CAKeyframeAnimation. 错误已修复,感谢评论:)



Or you can use this if you want more parameters (in swift 5) :

或者,如果您想要更多参数(在 swift 5 中),您可以使用它:

public extension UIView {

    func shake(count : Float = 4,for duration : TimeInterval = 0.5,withTranslation translation : Float = 5) {

        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.repeatCount = count
        animation.duration = duration/TimeInterval(animation.repeatCount)
        animation.autoreverses = true
        animation.values = [translation, -translation]
        layer.add(animation, forKey: "shake")
    }  
}

You can call this function on any UIView, UIButton, UILabel, UITextView etc. This way

你可以在任何 UIView、UIButton、UILabel、UITextView 等上调用这个函数。这样

yourView.shake()

Or this way if you want to add some custom parameters to the animation:

或者,如果您想向动画添加一些自定义参数,请使用这种方式:

yourView.shake(count: 5, for: 1.5, withTranslation: 10)

回答by Corey Pett

I think all of these are dangerous.

我认为所有这些都是危险的。

If your shake animation is based on a user action and that user action is triggered while animating.

如果您的摇晃动画基于用户操作,并且该用户操作在动画制作时被触发。

CRAAAAAASH

CRAAAAASH

Here is my way in Swift 4:

这是我在Swift 4 中的方式

static func shake(view: UIView, for duration: TimeInterval = 0.5, withTranslation translation: CGFloat = 10) {
    let propertyAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 0.3) {
        view.transform = CGAffineTransform(translationX: translation, y: 0)
    }

    propertyAnimator.addAnimations({
        view.transform = CGAffineTransform(translationX: 0, y: 0)
    }, delayFactor: 0.2)

    propertyAnimator.startAnimation()
}

Maybe not the cleanest, but this method can be triggered repeatedly and is easily understood

可能不是最干净的,但是这个方法可以反复触发,很容易理解

Edit:

编辑:

I am a huge proponent for usage of UIViewPropertyAnimator. So many cool features that allow you to make dynamic modifications to basic animations.

我非常支持使用 UIViewPropertyAnimator。这么多很酷的功能,让您可以对基本动画进行动态修改。

Here is another example to add a red border while the view is shaking, then removing it when the shake finishes.

这是在视图抖动时添加红色边框的另一个示例,然后在抖动完成时将其删除。

static func shake(view: UIView, for duration: TimeInterval = 0.5, withTranslation translation: CGFloat = 10) {
    let propertyAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 0.3) {
        view.layer.borderColor = UIColor.red.cgColor
        view.layer.borderWidth = 1
        view.transform = CGAffineTransform(translationX: translation, y: 0)
    }

    propertyAnimator.addAnimations({
        view.transform = CGAffineTransform(translationX: 0, y: 0)
    }, delayFactor: 0.2)

    propertyAnimator.addCompletion { (_) in
        view.layer.borderWidth = 0
    }

    propertyAnimator.startAnimation()
}

回答by Hardik Thakkar

Swift 5.0

斯威夫特 5.0

extension UIView {
    func shake(){
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.07
        animation.repeatCount = 3
        animation.autoreverses = true
        animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 10, y: self.center.y))
        animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 10, y: self.center.y))
        self.layer.add(animation, forKey: "position")
    }
}

To use

使用

self.vwOffer.shake()

回答by Dmitriiy Mitrophanskiy

extension CALayer {

    func shake(duration: NSTimeInterval = NSTimeInterval(0.5)) {

        let animationKey = "shake"
        removeAnimationForKey(animationKey)

        let kAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        kAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        kAnimation.duration = duration

        var needOffset = CGRectGetWidth(frame) * 0.15,
        values = [CGFloat]()

        let minOffset = needOffset * 0.1

        repeat {

            values.append(-needOffset)
            values.append(needOffset)
            needOffset *= 0.5
        } while needOffset > minOffset

        values.append(0)
        kAnimation.values = values
        addAnimation(kAnimation, forKey: animationKey)
    }
}

How to use:

如何使用:

[UIView, UILabel, UITextField, UIButton & etc].layer.shake(NSTimeInterval(0.7))

回答by Vahid

Swift 5

斯威夫特 5

Safe (non crash) shake extension for Corey Pettanswer:

安全(非崩溃)震动扩展以获取Corey Pett答案:

extension UIView {
    func shake(for duration: TimeInterval = 0.5, withTranslation translation: CGFloat = 10) {
        let propertyAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 0.3) {
            self.transform = CGAffineTransform(translationX: translation, y: 0)
        }

        propertyAnimator.addAnimations({
            self.transform = CGAffineTransform(translationX: 0, y: 0)
        }, delayFactor: 0.2)

        propertyAnimator.startAnimation()
    }
}

回答by ricardopereira

I tried some of the available solutions but none of them was handling the full shakeanimation: moving from left to right and get back to the original position.

我尝试了一些可用的解决方案,但没有一个能够处理完全震动的动画:从左向右移动并回到原始位置。

So, after some investigation I found the right solution that I consider to be a successful shake using UIViewPropertyAnimator.

因此,经过一番调查,我找到了正确的解决方案,我认为使用UIViewPropertyAnimator.

func shake(completion: (() -> Void)? = nil) {
    let speed = 0.75
    let time = 1.0 * speed - 0.15
    let timeFactor = CGFloat(time / 4)
    let animationDelays = [timeFactor, timeFactor * 2, timeFactor * 3]

    let shakeAnimator = UIViewPropertyAnimator(duration: time, dampingRatio: 0.3)
    // left, right, left, center
    shakeAnimator.addAnimations({
        self.transform = CGAffineTransform(translationX: 20, y: 0)
    })
    shakeAnimator.addAnimations({
        self.transform = CGAffineTransform(translationX: -20, y: 0)
    }, delayFactor: animationDelays[0])
    shakeAnimator.addAnimations({
        self.transform = CGAffineTransform(translationX: 20, y: 0)
    }, delayFactor: animationDelays[1])
    shakeAnimator.addAnimations({
        self.transform = CGAffineTransform(translationX: 0, y: 0)
    }, delayFactor: animationDelays[2])
    shakeAnimator.startAnimation()

    shakeAnimator.addCompletion { _ in
        completion?()
    }

    shakeAnimator.startAnimation()
}

Final result:

最后结果:

shake-animation-result

摇动动画结果

回答by Rana Ali Waseem

func shakeTextField(textField: UITextField)
{
    let animation = CABasicAnimation(keyPath: "position")
    animation.duration = 0.07
    animation.repeatCount = 3
    animation.autoreverses = true
    animation.fromValue = NSValue(cgPoint: CGPoint(x: textField.center.x - 10, y: textField.center.y))
    animation.toValue = NSValue(cgPoint: CGPoint(x: textField.center.x + 10, y: textField.center.y))
    textField.layer.add(animation, forKey: "position")

    textField.attributedPlaceholder = NSAttributedString(string: textField.placeholder ?? "",
                                                         attributes: [NSAttributedStringKey.foregroundColor: UIColor.red])

}

//write in base class or any view controller and use it

//写入基类或任何视图控制器并使用它

回答by Alessandro Ornano

This is based on CABasicAnimation, it contain also an audio effect :

这是基于 CABasicAnimation,它还包含一个音频效果:

extension UIView{
    var audioPlayer = AVAudioPlayer()
    func vibrate(){
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.05
        animation.repeatCount = 5
        animation.autoreverses = true
        animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 5.0, self.center.y))
        animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 5.0, self.center.y))
        self.layer.addAnimation(animation, forKey: "position")
        // audio part
        do {
            audioPlayer =  try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(mySoundFileName, ofType: "mp3")!))
            audioPlayer.prepareToPlay()
            audioPlayer.play()

        } catch {
            print("? Error playing vibrate sound..")
        }
    }
}