2017-12-03 239 views
1

我試圖做一個「記錄按鈕」這是一個UIView與我的應用程序的手勢識別器。目前我正在實現的功能是,當我點擊視圖時,我想縮小內圈(紅圈),但最終會出現意想不到的變形。我用UIView.animate功能來做到這一點,下面是我的代碼相關:Swift: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

你未定義的行爲..您正在將視圖應用到視圖..但在佈局子視圖中,您正通過setupFillView函數修改其框架。您無法這樣做。轉換已經修改了視圖的框架。因此,當使用原始的BasicAnimation時,我們總是修改圖層而不是框架的邊界和位置。 接下來,您將在佈局子視圖中添加視圖..這將導致遞歸,因爲視圖將再次佈局.. – Brandon

+0

@Brandon感謝您的回覆。我仍然感到困惑。我對這個主題沒有多少資源,只是從幾個教程彙編代碼。你能告訴我如何糾正它嗎? –

回答

2

問題是,您正在應用一個轉換,它將再次觸發layoutSubviews,因此framefillView正在應用於轉換後的視圖。

至少有兩個選項:

  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將不會觸發RecordViewlayoutSubviews

    @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調用或有的東西。

  • 完全不相關,但傳遞給completion的參數UIView.animate(withDuration:animations:completion:)是布爾值,指示動畫是否爲finished。如果您不打算使用該布爾值,則應該使用_,而不是(true)

  • 長期來看,我建議將「觸摸」/手勢相關的東西移到RecordButton中。我很容易想象你會變得更加奇特:例如(「縮小」按鈕以呈現「壓低」的氛圍),另一個用於「擡起」(例如,取消按下按鈕並將圓形「記錄」按鈕轉換爲方塊「停止」按鈕)。然後,您可以通過該按鈕通知視圖控制器何時發生重大事件(例如記錄或停止識別),但會降低視圖控制器本身的複雜性。

    protocol RecordViewDelegate: class { 
        func didTapRecord() 
        func didTapStop() 
    } 
    
    @IBDesignable 
    class RecordView: UIView { 
    
        @IBInspectable 
        var borderColor: UIColor = .clear { didSet { layer.borderColor = borderColor.cgColor } } 
    
        @IBInspectable 
        var borderWidth: CGFloat = 20 { didSet { layer.borderWidth = borderWidth; setNeedsLayout() } } 
    
        weak var delegate: RecordViewDelegate? 
    
        var isRecordButton = true 
    
        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) 
        } 
    
        private var radius: CGFloat { return min(bounds.width, bounds.height)/2 - borderWidth } 
    
        override func layoutSubviews() { 
         super.layoutSubviews() 
    
         containerView.frame = bounds 
    
         fillView.frame = CGRect(origin: CGPoint(x: bounds.midX - radius, y: bounds.midY - radius), 
               size: CGSize(width: radius * 2, height: radius * 2)) 
         fillView.layer.cornerRadius = isRecordButton ? radius : 0 
        } 
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 
         UIView.animate(withDuration: 0.25) { 
          self.fillView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 
          self.fillView.backgroundColor = UIColor(red: 0.75, green: 0, blue: 0, alpha: 1) 
         } 
        } 
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 
         if let touch = touches.first, containerView.bounds.contains(touch.location(in: containerView)) { 
          if isRecordButton { 
           delegate?.didTapRecord() 
          } else { 
           delegate?.didTapStop() 
          } 
          isRecordButton = !isRecordButton 
         } 
    
         UIView.animate(withDuration: 0.25) { 
          self.fillView.transform = .identity 
          self.fillView.backgroundColor = UIColor.red 
          self.fillView.layer.cornerRadius = self.isRecordButton ? self.radius : 0 
         } 
    
        } 
    } 
    

    國債收益率:

    enter image description here

+0

當我實現它,甚至直接複製代碼,我看不到layer.cornerRadius得到動畫。然後在[另一個線程](https://stackoverflow.com/questions/5948167/uiview-animatewithduration-doesnt-animate-cornerradius-variation),有人說核心動畫應該用來爲動畫cornerRadius。你怎麼能意識到這與UIKit?或者在我的代碼中錯過了其他東西? –

+0

它在iOS 11中具有動畫效果,但在早期版本中,您必須使用CoreAnimation或其他技術。 – Rob