iOS動畫:UIViewPropertyAnimator動畫之交互(16)

UIViewPropertyAnimator對象有三個屬性:
image_1
isRunning:只讀屬性,表明動畫是否處於運動狀態,默認值爲false,當調用startAnimation()時變爲true,如果動畫暫停(paused)、停止(stopped)、完成(finish),它將再次變爲false。
isReversed:可讀可寫屬性,默認值爲false,當設置爲true時,動畫將反轉執行到初始狀態。
state:只讀屬性,默認值爲inactive,這意味着你剛剛創建動畫還沒有調用任何方法。
調用以下方法會變成active狀態:
調用startAnimation()來啓動動畫;
在沒有開始動畫的情況下調用pauseAnimation();
設置fractionComplete屬性將動畫“倒回”到某個位置。

動畫完成後,state將切換回inactive狀態。如果你調用stopAnimation(),state屬性將變成stopped狀態,在這種狀態下,你唯一能做的就是停止整個動畫,調用finishAnimation(at:)將動畫變回inactive狀態。

UIViewPropertyAnimator只能按照特定的順序切換狀態,你不能從inactive切換到stopped,也不能從stopped切換到active。

UIViewPropertyAnimator在iOS11之後有一個新的屬性pausesOnCompletion,當設置爲true時,一旦動畫運行完成,它將不自動停止而是進入暫停狀態,這時候你將可以從暫停狀態繼續做你後續的工作。

流程圖如下
image_2

3D touch交互動畫(iOS10之後)

3D touch交互主要使用UIPreviewInteractionDelegate協議的方法,這裏不介紹3D touch如何使用,僅僅介紹3D touch動畫。

  public func previewInteractionDidCancel(_ previewInteraction: UIPreviewInteraction) {
    owner.cancelPreview()
  }
  
  func previewInteractionShouldBegin(_ previewInteraction: UIPreviewInteraction) -> Bool {
    
    if let indexPath = collectionView?.indexPathForItem(at: previewInteraction.location(in: collectionView!)),
      let cell = collectionView?.cellForItem(at: indexPath) as? IconCell {
      
      owner.startPreview(for: cell.icon)
    }
    return true
  }
  
  func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition transitionProgress: CGFloat, ended: Bool) {
    owner.updatePreview(percent: transitionProgress)
    
    if ended {
      owner.finishPreview()
    }
  }

我們將在應用內實現這種效果:
image_3
在AnimatorFactory.swift中創建動畫:

  static func grow(view: UIVisualEffectView, blurView: UIVisualEffectView) -> UIViewPropertyAnimator {
    view.contentView.alpha = 0
    view.transform = .identity
    let animator = UIViewPropertyAnimator(duration: 0.5, curve: .easeIn)
    
    animator.addAnimations {
      blurView.effect = UIBlurEffect(style: UIBlurEffectStyle.dark)
      view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
    }
    animator.addCompletion { (_) in
      blurView.effect = UIBlurEffect(style: UIBlurEffectStyle.dark)
    }
    return animator
  }

在LockScreenViewController.swift中添加屬性:

  var startFrame: CGRect?//動畫的初始座標
  var previewView: UIView?//icon的快照,用於3D touch顯示
  var previewAnimator: UIViewPropertyAnimator?//動畫
  let previewEffectView = IconEffectView(blur: .extraLight)//重按後icon的邊框邊框
  let blurView = UIVisualEffectView(effect: nil)//3D touch後的背景模糊效果

3D touch開始時,設置屬性值:

  func startPreview(for forView: UIView) {
    previewView?.removeFromSuperview()//移除舊的icon快照
    previewView = forView.snapshotView(afterScreenUpdates: false)
    view.insertSubview(previewView!, aboveSubview: blurView)//
    previewView?.frame = forView.convert(forView.bounds, to: view)//設置快照的座標在原icon之上
    startFrame = previewView?.frame//記住初始座標
    addEffectView(below: previewView!)//將icon邊框效果插入icon快照下面
    
    previewAnimator = AnimatorFactory.grow(view: previewEffectView, blurView: blurView)//創建動畫,動畫還未開始
  }
  
    func addEffectView(below forView: UIView) {
    previewEffectView.removeFromSuperview()
    previewEffectView.frame = forView.frame
    forView.superview?.insertSubview(previewEffectView, belowSubview: forView)
  }

3D touch更新回調後更新動畫到指定位置:

  func updatePreview(percent: CGFloat) {
    previewAnimator?.fractionComplete = max(0.01, min(0.99, percent))
  }

下面我們來實現動畫的完成和取消方法。

繼續在AnimatorFactory.swift中添加動畫:

  static func reset(frame: CGRect, view: UIVisualEffectView, blurView: UIVisualEffectView) -> UIViewPropertyAnimator {//還原
    return UIViewPropertyAnimator(duration: 0.5, dampingRatio: 0.7, animations: {
      view.transform = .identity
      view.frame = frame
      view.contentView.alpha = 0
      
      blurView.effect = nil
    })
  }

在LockScreenViewController中實現3D touch還原的動畫:

  func cancelPreview() {//用戶輕按時還原動畫
    if let previewAnimator = previewAnimator {
      previewAnimator.isReversed = true
      previewAnimator.startAnimation()
    }
  }

還原的動畫就完成啦,下面添加完成的動畫。
在AnimatorFactory.swift中的grow方法,我們已經添加了完成的block,我們只需對其進行修改即可:

    animator.addCompletion { position in
      switch position {
        case .start:
          blurView.effect = nil
        case .end:
          blurView.effect = UIBlurEffect(style: .dark)
        default: break
      }
    }

position是一個UIViewAnimatingPosition類型的枚舉值,分別代表動畫是在開始停止,結束後停止,還是當前位置停止。 通常你都會收到結束的枚舉值。它有三個枚舉值:start、end、current,如果你的動畫自然完成或者以其他方式結束,它將返回end值;如果你反轉動畫並在開始的位置完成,那麼它將返回start;如果你中途停止動畫並在那裏完成,它將返回current值。

動畫在開始位置完成(即輕按3D touch)後需要移除icon快照和邊框,在LockScreenViewController的cancelPreview方法中添加代碼:

	previewAnimator.addCompletion { (position) in
        switch position {
          case .start:
            self.previewView?.removeFromSuperview()
            self.previewEffectView.removeFromSuperview()
        default: break
        }
    }

addCompletion可以添加多個,並不會對之前添加的有任何影響。
3D touch的完成和還原動畫就實現啦。下面我們來設置3D touch彈出的菜單(即之前添加的icon快照的邊框previewEffectView,我們只需要改變其位置和大小即可)。

在AnimatorFactory中添加動畫:

  static func complete(view: UIVisualEffectView) -> UIViewPropertyAnimator {
    return UIViewPropertyAnimator(duration: 0.3, dampingRatio: 0.7, animations: {
      view.contentView.alpha = 1
      view.transform = .identity
      view.frame = CGRect(x: view.frame.minX - view.frame.minX/2.5, y: view.frame.maxY - 140, width: view.frame.width + 120, height: 60)
    })
  }

在LockScreenViewController中3D touch完成的回調裏添加動畫:

  func finishPreview() {
    previewAnimator?.stopAnimation(false)//false表示將動畫停止,需要手動調用finishAnimation完成動畫,true表示動畫停止,並將動畫置於inactive狀態,不需要調用finishAnimation
    previewAnimator?.finishAnimation(at: .end)
    previewAnimator = nil
    AnimatorFactory.complete(view: previewEffectView).startAnimation()
  }

下面添加點擊背景取消3D touch的動畫。
在finishPreview中添加代碼:

    blurView.effect = UIBlurEffect(style: .dark)
    blurView.isUserInteractionEnabled = true
    blurView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissMenu)))

設置dimissMenu的動畫:

  @objc func dismissMenu() {
    let reset = AnimatorFactory.reset(frame: startFrame!, view: previewEffectView, blurView: blurView)
    reset.addCompletion { (_) in
      self.previewEffectView.removeFromSuperview()
      self.previewView?.removeFromSuperview()
      self.blurView .isUserInteractionEnabled = false
    }
    reset.startAnimation()
  }

運行效果:
image_4

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章