2017-03-17 3 views
0

큰 프로젝트에 통합하기 위해 뷰 컨트롤러 사용자 지정 애니메이션을 일부 재생하는 간단한 샘플 프로젝트가 있지만 몇 가지 문제점을 발견했습니다.내 하위 뷰가 뷰 컨트롤러로 애니메이션되지 않습니다.

두 개의 간단한보기 컨트롤러가 있는데, 두 번째보기 컨트롤러는 첫 번째 단추에 두 번째보기 컨트롤러를 표시하고 사용자 지정 애니메이션이 있습니다. 두 번째에는 레이블이 있고보기 컨트롤러를 애니메이션 장면에 애니메이션으로 표시하는 해제 버튼이 있습니다.

현재 컨트롤러를 표시 할 때 현재 버튼을 클릭하면 현재 표시된 컨트롤러의 하위보기, 보기 컨트롤러가 움직이기 전에 화면에 나타나지만, VC를 닫을 때, 모든 하위보기는 해제 된보기 제어기와 함께 진행됩니다. (발표) 내보기 컨트롤러이 나는 내부 기본 자동 레이아웃 (제안 제약으로 재설정)이 파단에 애니메이션을하지 않는 이유는 정당성을 찾을 수 없습니다

과 의견을했습니다에

내가 예상했던대로보기 컨트롤러.

GIF

CODE : 뷰 컨트롤러 1 (제시 VC)

수입 UIKit

클래스의 ViewController : 아래

는 무슨 일이 일어나고 있는지 보여주는 GIF, 소스 코드를 간다의 UIViewController를 , UIViewControllerTransitioningDelegate {

var animator = Animator() 


override func viewDidLoad() { 
    super.viewDidLoad() 
} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 


@IBAction func presentButton(_ sender: Any) { 
    let storyboard = UIStoryboard(name: "Main", bundle: nil); 
    let vc = storyboard.instantiateViewController(withIdentifier: "vc2") as! ViewController2 

    vc.transitioningDelegate = self 
    vc.modalPresentationStyle = .custom // chama as funções à parte 

    present(vc, animated: true, completion: nil) 
} 




func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 
    animator.transitioningMode = .Present // sabe que está em presenting mode 
    return animator 
} 

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 
    animator.transitioningMode = .Dismiss // Sabe que está em dismissing mode 
    return animator 
} 

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 
    return CustomPresentationController(presentedViewController: presented, presenting: presenting) 
} 




} 

CODE : 뷰 제어기 (2) (표시 VC)

클래스 ViewController2

수입 UIKit : UIViewController에 {

override func viewDidLoad() { 
    super.viewDidLoad() 
} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
} 



@IBAction func dismissButton(_ sender: Any) { 
    dismiss(animated: true, completion: nil) 
} 

} 

CODE : CustomPresentationController

import UIKit 

수입 재단

클래스 쿠스 tomPresentationController : UIPresentationController {

override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController!) { 
    super.init(presentedViewController: presentedViewController, presenting: presentingViewController) 
} 


override var frameOfPresentedViewInContainerView: CGRect { 

    // arranca a 0 
    var presentedViewFrame = CGRect.zero 

    // Calcula os bounds do container 
    let containerBounds = self.containerView?.bounds 

    // Recalcula o size 
    presentedViewFrame.size = CGSize(width: (containerBounds?.size.width)! , height: ((containerBounds?.size.height)! * 0.90)) 

    presentedViewFrame.origin.x = 0 
    presentedViewFrame.origin.y = (containerBounds?.size.height)! * 0.1 

    return presentedViewFrame 
} 


} 

CODE : 애니메이터 클래스

수입 재단 수입 UIKit

클래스 애니메이터 : NSObject의, UIViewControllerAnimatedTransitioning {

enum Status { 
    case Present 
    case Dismiss 
} 
var transitioningMode: Status = .Present 
var presentDuration = 1.0 
var dismissDuration = 0.3 


// Tempo da animação 
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 
    if (transitioningMode == .Present) { 
     return presentDuration 
    } else { 
     return dismissDuration 
    } 
} 



func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 

    // Get the set of relevant objects. 
    let containerView = transitionContext.containerView 

    guard 
     let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), 
     let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) 
     else { 
      print("Returning animateTransition VC") 
      return 
    } 

    let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) 
    let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) 



    // Set up some variables for the animation. 
    let containerFrame:  CGRect = containerView.frame 
    var toViewStartFrame: CGRect = transitionContext.initialFrame(for: toVC) 
    let toViewFinalFrame: CGRect = transitionContext.finalFrame(for: toVC) 
    var fromViewFinalFrame: CGRect = transitionContext.finalFrame(for: fromVC) 

    // Set up animation parameters. 
    if (transitioningMode == .Present) { 
     // Modify the frame of the presented view so that it starts 
     // offscreen at the lower-right corner of the container. 
     toViewStartFrame.origin.x = 0//containerFrame.size.width 
     toViewStartFrame.origin.y = containerFrame.size.height * 0.1 


    } else { 
     // Modify the frame of the dismissed view so it ends in 
     // the lower-right corner of the container view. 
     fromViewFinalFrame = CGRect(x: containerFrame.size.width, 
            y: containerFrame.size.height, 
            width: (toVC.view.frame.size.width), 
            height: (toVC.view.frame.size.height)) 



    } 

    if (transitioningMode == .Present) { 
     // Always add the "to" view to the container. 
     // And it doesn't hurt to set its start frame. 
     containerView.addSubview(toView!) 
     toView?.frame = toViewStartFrame 
    } 

    // Animate using the animator's own duration value. 
    UIView.animate(withDuration: presentDuration, animations: { 

     if (self.transitioningMode == .Present) { 
      // Move the presented view into position. 
      toView?.frame = toViewFinalFrame 
     } 
     else { 
      // Move the dismissed view offscreen. 
      fromView?.frame = fromViewFinalFrame 
     } 
    }) { (finished) in 
     let success = !(transitionContext.transitionWasCancelled) 
     // After a failed presentation or successful dismissal, remove the view. 
     if ((self.transitioningMode == .Present && !success) || (self.transitioningMode == .Dismiss && success)) { 
      toView?.removeFromSuperview() 
     } 

     // Notify UIKit that the transition has finished 
     transitionContext.completeTransition(success) 

    } 

} 



} 
+0

파란색보기는 현재보기 컨트롤러의 맨 아래에 있어야합니다. 나는 문제를 안다. 그러나 코드를 보여 주려한다면, 나는 당신의 유스 케이스에 맞기를 바란다. 내비게이션 막대 또는 무언가가 – agibson007

+0

이고 현재 상황에서입니까? – agibson007

+0

애니메이션 효과 (어디서 왔는지, 끝나는 곳은별로 중요하지 않지만 나중에 변경하겠습니다) –

답변

1

나는 자상 걸릴 것입니다 좋아 당신을 도울 때. 첫 번째 이유는 그것이 작동하지 않는 이유는 autolayout이 당신의 의견을 가지고 있지만 기본적으로 크기가 0 인 프레임을 가지고 있기 때문입니다. 이것을 보여주기 위해 문제가되는 컨트롤러로 이동하여 서브 뷰를 클리핑하면 트랜지션 중에 나타나지 않을 것입니다. 이제 프레임을 옮기고 싶을 뿐이므로 프레임을 옮기지 않고 코드를 제거 할 수 있습니다. 여기에 그것이 나를 위해 어떻게 생겼는지입니다.

import UIKit 

class ViewController: UIViewController,UIViewControllerTransitioningDelegate { 

    var animator = Animator() 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

    @IBAction func presentModally(_ sender: Any) { 
     self.performSegue(withIdentifier: "modal", sender: nil) 
    } 

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 
     if segue.identifier == "modal"{ 
      let dvc = segue.destination 
      dvc.transitioningDelegate = self 
      dvc.modalPresentationStyle = .overCurrentContext 
     } 
    } 

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 
     animator.transitioningMode = .Present // sabe que está em presenting mode 
     return animator 
    } 

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 
     animator.transitioningMode = .Dismiss // Sabe que está em dismissing mode 
     return animator 
    } 

} 

사용자 정의 프리젠 테이션 컨트롤러를 삭제하십시오. 나는 정직하게 그것을 필요로하지 않는다.

이제 애니메이터.

import UIKit 

class Animator: NSObject,UIViewControllerAnimatedTransitioning { 
    enum Status { 
     case Present 
     case Dismiss 
    } 
    var transitioningMode: Status = .Present 
    var presentDuration = 1.0 
    var dismissDuration = 0.3 


    // Tempo da animação 
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 
     if (transitioningMode == .Present) { 
      return presentDuration 
     } else { 
      return dismissDuration 
     } 
    } 


    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 

     // Get the set of relevant objects. 
     let containerView = transitionContext.containerView 

     guard 
      let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), 
      let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) 
      else { 
       print("Returning animateTransition VC") 
       return 
     } 

     let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) 
     let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) 

     // Set up some variables for the animation. 
     let containerFrame:  CGRect = containerView.frame 
     let toViewFinalFrame: CGRect = transitionContext.finalFrame(for: toVC) 
     var fromViewFinalFrame: CGRect = transitionContext.finalFrame(for: fromVC) 

     // Set up animation parameters. 
     if (transitioningMode == .Present) { 
      let anchor = toViewFinalFrame.origin 
      toView?.layer.anchorPoint = anchor 
      toView?.layer.position = anchor 
      toView?.transform = CGAffineTransform(scaleX: 0, y: 1) 
      //another posibility 
      //toView?.transform = CGAffineTransform(translationX: -containerView.bounds.width, y: -containerView.bounds.height) 
     } else { 
      // Modify the frame of the dismissed view so it ends in 
      // the lower-right corner of the container view. 
      fromViewFinalFrame = CGRect(x: containerFrame.size.width, 
             y: containerFrame.size.height, 
             width: (toVC.view.frame.size.width), 
             height: (toVC.view.frame.size.height)) 
     } 

     if (transitioningMode == .Present) { 
      // Always add the "to" view to the container. 
      // And it doesn't hurt to set its start frame. 
      containerView.addSubview(toView!) 
      // toView?.frame = toViewStartFrame 
     } 

     // Animate using the animator's own duration value. 
     UIView.animate(withDuration: presentDuration, animations: { 

      if (self.transitioningMode == .Present) { 
       // Move the presented view into position. 
       toView?.transform = .identity 
      } 
      else { 
       // Move the dismissed view offscreen. 
       fromView?.frame = fromViewFinalFrame 
      } 
     }) { (finished) in 
      let success = !(transitionContext.transitionWasCancelled) 
      // After a failed presentation or successful dismissal, remove the view. 
      if ((self.transitioningMode == .Present && !success) || (self.transitioningMode == .Dismiss && success)) { 
       toView?.removeFromSuperview() 
      } 

      // Notify UIKit that the transition has finished 
      transitionContext.completeTransition(success) 

     } 

    } 

} 

그게 전부입니다. 건배.

+0

나는 그것이 내가 미래라면 커스텀 프레젠테이션 클래스가 필요하다고 생각했습니다. 뷰 컨트롤러 크기를 변경하고 싶었습니다. 나는 당신이 쓴 것을 시도 할 것이고 나는 그것을 곧 피드백 할 것이다. 도움을 주셔서 감사합니다 :) –

+0

당신은 사용자 지정 프레 젠 테이션 컨트롤러를 유지할 수 있지만 프레임을 원하는 정확한 크기를 반환하고 번역 또는 스케일을 사용하여 장소로 이동합니다. 단지 2 센트. 애니메이션을하고 실제로 프레임을 변경하려고 할 때 일반적으로 스냅 샷을 찍어 원본을 숨기고 마지막 스냅 샷을 제거합니다. 그러나 뷰 또는 하위 뷰의 스냅 숏 만 애니메이션으로 표시합니다. 그런 식으로 저는 실제 시각의 틀을 결코 조작하지 못합니다. 희망이 도움이됩니다. – agibson007

+0

멋진 팁. mail.app의 iOS 10.3에서 새 애니메이션을 보았습니까? 새 전자 메일을 만들 때보기 컨트롤러는 아래쪽에서 비롯되었지만 위쪽에는 맞지 않습니다. 크기의 90 %와 비슷합니다. 배경보기 컨트롤러가 조금씩 확장되어 두 가지 모두를 볼 수 있습니다. 그게 내가 여기서 만들려고하는거야;) –