Swift:UIView.animate意外地工作

时间:2017-12-03 06:20:54

标签: ios swift animation uiview addsubview

我正在尝试制作一个“记录按钮”,这是一个带有手势识别器的UIView,适用于我的应用。现在我正在实现这个功能,当我点击视图时,我想缩小内圈(红圈),但最终会出现意外的转换。我使用UIView.animate函数来执行此操作,以下是我的相关代码:

import UIKit

@IBDesignable
class RecordView: UIView {

    @IBInspectable
    var borderColor: UIColor = UIColor.clear {
        didSet {
            self.layer.borderColor = borderColor.cgColor
        }
    }

    @IBInspectable
    var borderWidth: CGFloat = 20 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable
    var cornerRadius: CGFloat = 100 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }

    private var fillView = UIView()

    private func setupFillView() {
        let radius = (self.cornerRadius - self.borderWidth) * 0.95
        fillView.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: radius * 2, height: radius * 2))
        fillView.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
        fillView.layer.cornerRadius = radius
        fillView.backgroundColor = UIColor.red
        self.addSubview(fillView)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        setupFillView()
    }

    func didClick() {
        UIView.animate(withDuration: 1.0, animations: {
            self.fillView.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
        }) { (true) in
            print()
        }
    }

}

在我的ViewController中,我有:

@IBAction func recoreViewTapped(_ sender: UITapGestureRecognizer) {
        recordView.didClick()
    }

然而它最终会产生这种效果: https://media.giphy.com/media/3ohjUQrWt7vfIxzBrG/giphy.gif。我是初学者,真的不知道我的代码有什么问题?

1 个答案:

答案 0 :(得分:2)

问题是您正在应用转换,这会再次触发layoutSubviews,因此frame fillView的重置将应用于转换后的视图

至少有两个选择:

  1. 您可以转换整个RecordButton视图:

    @IBDesignable
    class RecordView: UIView {
    
        @IBInspectable
        var borderColor: UIColor = .clear { didSet { layer.borderColor = borderColor.cgColor } }
    
        @IBInspectable
        var borderWidth: CGFloat = 20 { didSet { layer.borderWidth = borderWidth; setNeedsLayout() } }
    
        private var fillView = UIView()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            configure()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            configure()
        }
    
        private func configure() {
            fillView.backgroundColor = .red
            self.addSubview(fillView)
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            let radius = min(bounds.width, bounds.height) / 2  - borderWidth
            fillView.frame = CGRect(origin: CGPoint(x: bounds.midX - radius, y: bounds.midY - radius),
                                    size: CGSize(width: radius * 2, height: radius * 2))
            fillView.layer.cornerRadius = radius
        }
    
        func didClick() {
            UIView.animate(withDuration: 1.0, animations: {
                self.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
            }, completion: { _ in
                print("completion", #function)
            })
        }
    
    }
    
  2. 或者您可以将fillView放入容器中,然后fillView赢得的转换会触发layoutSubviews的{​​{1}} :

    RecordView
  3. 顺便说一下,还有其他几件小事:

    • 正如您和我在别处讨论过的那样,应该从@IBDesignable class RecordView: UIView { @IBInspectable var borderColor: UIColor = .clear { didSet { layer.borderColor = borderColor.cgColor } } @IBInspectable var borderWidth: CGFloat = 20 { didSet { layer.borderWidth = borderWidth; setNeedsLayout() } } private var fillView = UIView() private var containerView = UIView() override init(frame: CGRect) { super.init(frame: frame) configure() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() } private func configure() { fillView.backgroundColor = .red self.addSubview(containerView) containerView.addSubview(fillView) } override func layoutSubviews() { super.layoutSubviews() containerView.frame = bounds let radius = min(bounds.width, bounds.height) / 2 - borderWidth fillView.frame = CGRect(origin: CGPoint(x: bounds.midX - radius, y: bounds.midY - radius), size: CGSize(width: radius * 2, height: radius * 2)) fillView.layer.cornerRadius = radius } func didClick() { UIView.animate(withDuration: 1.0, animations: { self.fillView.transform = CGAffineTransform(scaleX: 0.6, y: 0.6) }, completion: { _ in print("completion", #function) }) } } 调用初始配置(例如添加子视图)。但是应该从init调用任何frame / bounds个临时内容。

    • 完全不相关,但传递给layoutSubviews的{​​{1}}的参数是一个布尔值,表示动画是否为completion。如果您不打算使用该布尔值,则应使用UIView.animate(withDuration:animations:completion:),而不是finished

    • 从长远来看,我建议将"触摸" /手势相关的内容移动到RecordButton中。我很容易想象你会变得更加漂亮:例如一个动画,因为你"触地" (缩小按钮以呈现"压下" vibe)和另一个用于"抬起" (例如,取消按钮并将圆形"记录"按钮转换为方形"停止"按钮)。然后,您可以让按钮在发生重大事件时通知视图控制器(例如记录或停止识别),但会降低视图控制器本身的复杂性。

      _

      产量:

      enter image description here