ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • iOS) Appstore Transition 따라하기(2) - Present Transition
    iOS 2021. 1. 5. 13:49

    AppStore Transition 따라하기의 정수와 묘미인 transition 구성이다.

     

    우리가 만들 Transition은 총 4개이다.

    - Presentation

    - Present Transition

    - Dismiss Transition

    - interactive Transitioning

     

    분기를 위하여 Transition을 총괄 해주는 역할을 할 UIVIewControllerTransitioningDelegate를 채택한 AppContentTransitionController를 만들어 주자.

    import Foundation
    
    class AppContentTransitionController: NSObject, UIViewControllerTransitioningDelegate {
        
        var superViewcontroller: UIViewController?
        var indexPath: IndexPath?
        
        func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
            return nil
        }
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return nil
        }
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return nil
        }
        func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
            return nil
        }
    }
    

     

    위의 4개 함수를 채우는 것이 최종 목표이다.

     

    프로퍼티 superViewController와 indexPath는 transition 과정에서의 view의 애니메이션과 데이터를 위한 indexPath이다.

    cell의 데이터와 scrollView의 indicator, textView의 크기 조절을 위해 MenuVC의 didSelectItemAt 메소드에서 파라미터로 넘겨주자.

     

    넘어간 데이터들은 AppContentTransitionController에서 받아 다시 Animator로 넘겨진다.

    class AppStoreMenuViewController: UIViewController {
    
    	var appStoreTransition = AppContentTransitionController() // Transtion Animator 생성
    
    	...
    
    	func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            
            let cell = collectionView.cellForItem(at: indexPath) as! AppCollectionViewCell
            
            let appContentVC = AppContentViewController()
            
            // 데이터를 넘겨준다
            appStoreTransition.indexPath = indexPath
            appStoreTransition.superViewcontroller = appContentVC
            
            appContentVC.fetchData(model: model[indexPath.row])
            appContentVC.modalPresentationStyle = .custom
            appContentVC.transitioningDelegate = appStoreTransition
            appContentVC.modalPresentationCapturesStatusBarAppearance = true
            
            self.present(appContentVC, animated: true, completion: nil)
        }
    }

     

     

    다시 appStore를 보자.

     

    cell을 터치 했을 때 cell의 크기가 줄어 들었다가 transition이 시작 되는 것을 볼 수 있다.

     

    Cell Touch Animation

    cell의 움직임 부터 구현 해보자.

     

    Appstore Transition 따라하기(1)에서 구현 했던 AppCollectionViewCell에 touch event 기능을 override 하여 사용하면 될 것 같다.

     

    import UIKit
    import SnapKit
    
    class AppCollectionViewCell: UICollectionViewCell {
      
        //layout 구현부
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesBegan(touches, with: event)
            bounceAnimate(isTouched: true)
        }
        
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesEnded(touches, with: event)
            bounceAnimate(isTouched: false)
        }
        
        override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
            super.touchesCancelled(touches, with: event)
            bounceAnimate(isTouched: false)
        }
        
        private func bounceAnimate(isTouched: Bool) {
            
            if isTouched {
                AppCollectionViewCell.animate(withDuration: 0.5,
                               delay: 0,
                               usingSpringWithDamping: 1,
                               initialSpringVelocity: 1,
                               options: [.allowUserInteraction], animations: {
                                self.transform = .init(scaleX: 0.96, y: 0.96)
                                self.layoutIfNeeded()
                               }, completion: nil)
            } else {
                AppCollectionViewCell.animate(withDuration: 0.5,
                               delay: 0,
                               usingSpringWithDamping: 1,
                               initialSpringVelocity: 0,
                               options: .allowUserInteraction, animations: {
                                self.transform = .identity
                               }, completion: nil)
            }
        }
     
    }
    
    
    

     

     

    예상과 달리 cell을 터치 했을때 반응이 느리다. present 또한 크기의 줄어듬 없이 바로 진행 된다.

     

    collectionView의 isScrollEnabled를 false로 지정 해주었을 때는 자연스러운 애니메이션이 구현 되는거 보니 scrollView의 스크롤 애니메이션이 다른 애니메이션 보다 우선순위에 있어서 그런것 같다.

     

    - scrollView cell에 애니메이션 효과를 줄 때 반응 속도가 늦다.

    scrollView의 scrollEnable이 false일 땐 반응 속도가 빠르다 -> delaysContentTouches를 비활성화 해줌으로써 해결.

     

    Discussion

    If the value of this property is true, the scroll view delays handling the touch-down gesture until it can determine if scrolling is the intent. If the value is false, the scroll view immediately calls touchesShouldBegin(_:with:in:). The default value is true.

    See the class description for a fuller discussion.

     

    true일 경우 스크롤 애니메이션이 우선, false일 경우 touchBegan 메소드가 우선적으로 불리운다고 한다.

    이전 보다 민감한 터치와 훨씬 자연스러운 애니메이션 효과를 볼 수 있다.

     

    StatusBar Animation

    cell을 터치하고 새로운 view가 present 될 때 statusBar가 fame 밖으로 사라지는 animation을 볼 수 있다.

     

    statusBar를 다루는 방법은 이미 선언 되어있는 statusBar 변수들을 override 하여 사용한다.

     

    presented VC로 가서 다음과 같은 변수들을 리턴 해준다.

    class AppContentViewController: UIViewController, StatusBarAnimationViewController {
        
        var statusBarShouldBeHidden: Bool = false
        
        override var preferredStatusBarStyle: UIStatusBarStyle {
            return .default
        }
        override var prefersStatusBarHidden: Bool {
            return statusBarShouldBeHidden
        }
        override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
            return .slide
        }
       
       
       ...
       
       
       
    }

     

    preferredStatusBarStyle: UIStatusBarStyle

    statusBar의 스타일을 지정 해줄 수 있다. 

    디폴트, 밝은 스타일과 어두운 스타일이 있다.

     

    prefersStatusBarHidden: Bool

    statusBar 숨김을 이 프로퍼티를 통해 지정 해준다.

     

    preferredStatusBarUpdateAnimation: UIStatusBarAnimation

    prefersStatusBarHidden이 true일 때 어떠한 형식의 애니메이션으로 사라질지 지정 할 수 있는 프로퍼티이다.

    fade, none, slide가 있다.

     

     

    그 후 statusBar에 애니메이션 효과를 적용 시켜주기 위해 setNeedsStatusBarAppearanceUpdate()를 적절한 타이밍에 호출 해준다.

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            updateStatusBar(hidden: true, completion: nil)
        }
        
        func updateStatusBar(hidden: Bool, completion: ((Bool) -> Void)?) {
            statusBarShouldBeHidden = hidden
            UIView.animate(withDuration: 0.5) {
                self.setNeedsStatusBarAppearanceUpdate()
            }
        }

    위의 updateStatusBar 함수를 통해 statusBar의 애니메이션 속도를 지정 해주고 view가 생성 될 시점인 viewWillAppear에서 호출 해준다.

     

    마지막으로 presentingVC에서 presentedVC를 호출하는 시점에서 modalPresentationCapturesStatusBarAppearance 프로퍼티를 true로 전환 해준다.

    modalPresentationCapturesStatusBarAppearance

    Specifies whether a view controller, presented non-fullscreen, takes over control of status bar appearance from the presenting view controller.

     

    present방식이 full screen이 아닐때 statusBar의 appearance를 presented VC로 넘긴다고 한다.

     

    우린 custom present를 쓸거기 때문에 해당 프로퍼티를 true로 바꿔준다.

    class AppStoreMenuViewController: UIViewController {
    	func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            
            let cell = collectionView.cellForItem(at: indexPath) as! AppCollectionViewCell
           
            let appContentVC = AppContentViewController()
            
            appStoreTransition.indexPath = indexPath
            appStoreTransition.superViewcontroller = appContentVC
            
            appContentVC.fetchData(model: model[indexPath.row])
            appContentVC.modalPresentationStyle = .custom
            appContentVC.transitioningDelegate = appStoreTransition
            
            appContentVC.modalPresentationCapturesStatusBarAppearance = true // true로 설정
            
            self.present(appContentVC, animated: true, completion: nil)
        }
    }

     

     

     

    Presentation

    present가 진행 될때 cell뒤의 화면에 blur효과를 준다.

     

    보통 VC에서 하듯이 class 프로퍼티로 visualEffectView를 만들어 주고 containerView에 붙혀주자.

    import UIKit
    
    class AppcontentPresentaion: UIPresentationController {
        
        lazy var blurView: UIVisualEffectView = {
            var view = UIVisualEffectView(effect: nil)
            view.translatesAutoresizingMaskIntoConstraints = false
            return view
        }()
        
        override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
            super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        }
        
        override func presentationTransitionWillBegin() {
            super.presentationTransitionWillBegin()
            guard let containerView = containerView else  { fatalError() }
            
            containerView.insertSubview(blurView, at: 0)
            
            blurView.alpha = 0.0
            blurView.frame = containerView.frame
            
            containerView.layoutIfNeeded()
            
            guard let coordinator = presentedViewController.transitionCoordinator else {
                blurView.alpha = 1.0
                blurView.effect = UIBlurEffect(style: .light)
                return
            }
            coordinator.animate(alongsideTransition: { (animation) in
                self.blurView.alpha = 1.0
                self.blurView.effect = UIBlurEffect(style: .light)
                containerView.layoutIfNeeded()
            }, completion: nil)
            
        }
        override func dismissalTransitionWillBegin() {
            super.dismissalTransitionWillBegin()
            guard let coordinator = presentingViewController.transitionCoordinator else {
                blurView.alpha = 0.0
                return
            }
            coordinator.animate(alongsideTransition: { (animator) in
                self.blurView.alpha = 0.0
                self.containerView?.layoutIfNeeded()
            }, completion: nil)
        }
    }

     

     

    AppContentTransitionConroller

    import Foundation
    import UIKit
    
    class AppContentTransitionController: NSObject, UIViewControllerTransitioningDelegate {
        
        var superViewcontroller: UIViewController?
        var indexPath: IndexPath?
        
        func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
            return AppcontentPresentaion(presentedViewController: presented, presenting: presenting)
        }
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return nill
        }
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return nill
        }
        func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
            return nil
        }
    }

    첫번째 함수 획득

     

    Present Transition

    이번 Tranisition에서 가장 중요한건 cell이 튀어오르는 듯한 효과를 주는 것이다.

    animation 메소드 중 damping 파라미터를 사용하여 구현하며 같은 damping 수치가 적용 된 cell이여도 위치에 따라 적용되는 효과가 다르기 때문에 값은 고정이다. 자연스러운 tranisition을 위해 각 constraint의 animation 타이밍도 중요하다.

     

    우선 appstore의 transition을 보자.

     

     

    간단하게 cell의 top Constraints가 먼저 containerView의 top과 같아지고 height와 width가 확장되는 걸 볼 수 있다.

     

    Transition에서 가장 중요한 것은 자연스러운 연결이다.

    presentingVC의 cell와 presentedVC view의 자연스러운 연결

     

    가장 먼저 tranistion이 진행되는 View인 transitionContext의 containerView와 두 개의 VC를 다운캐스팅을 통해 가져오자.

    import Foundation
    import UIKit
    
    class AppContentPresentingAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    
        var targetIndexPath: IndexPath?
        var targetData: AppContentModel?
        
        init(indexPath: IndexPath) {
            super.init()
            targetIndexPath = indexPath
            targetData = model[indexPath.row]
        }
        
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1
        }
        
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            let containerView = transitionContext.containerView
            
            containerView.alpha = 1.0
            
            guard let fromVC = transitionContext.viewController(forKey: .from) as? TabBarViewController else { fatalError() }
            guard let appStoreMenuVC = fromVC.viewControllers![0] as? AppStoreMenuViewController else { fatalError() }
            
            guard let contentVC = transitionContext.viewController(forKey: .to) as? AppContentViewController else { fatalError() }
            guard let fromView = appStoreMenuVC.view else { fatalError() }
            guard let toView = contentVC.view else { fatalError() }
        
    }
    

     

    현재 rootVC는 tabBarController이기 때문에 tabBarController를 통해 appStoreMenuVC에 접근 하도록 하자.

     

    - Tabbar Transition Animation

    cell의 transition을 구현하기 전에 Tabbar의 animation도 잊지 말자.

     

    Transition이 진행 될 때 cell이 화면을 가득 매움에 따라 tabbar도 자연스럽게 아래로 사라진다.

    위의 fromVC를 통해 기존의 tabbar를 가져오고 새로운 fallingTabbar를 만들어 fromVC의 tabbar를 복사한 후 transition에 사용하도록 하자.

     

    이 때 주의 해야 할 점은 fromVC의 tabbar를 복사한다면 tabbar는 class 객체이기 때문에 falling Tabbar를 조작 할 경우 원래의 tabbar 또한 변경 된다는 점이다.

    이 점 때문에 원래의 tabbar과 똑같은 item 객체를 만들어서 falling Tabbaritems에 넣어준다.

     

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            let containerView = transitionContext.containerView
            
            containerView.alpha = 1.0
            
            guard let fromVC = transitionContext.viewController(forKey: .from) as? TabBarViewController else { fatalError() }
            guard let appStoreMenuVC = fromVC.viewControllers![0] as? AppStoreMenuViewController else { fatalError() }
            
            guard let contentVC = transitionContext.viewController(forKey: .to) as? AppContentViewController else { fatalError() }
            guard let fromView = appStoreMenuVC.view else { fatalError() }
            guard let toView = contentVC.view else { fatalError() }
            
            let targetTabbar = fromVC.tabBar
            let fallingTabbar = UITabBar(frame: targetTabbar.frame)
            
            let vcTabBarItem = UITabBarItem(title: "투데이", image: nil, tag: 0)
            let vc2TabBarItem = UITabBarItem(title: "설정", image: nil, tag: 1)
            
            vc2TabBarItem.isEnabled = false
            
            fallingTabbar.items = [vcTabBarItem, vc2TabBarItem]
            fallingTabbar.selectedItem = fallingTabbar.items?.first
    }

     

    - Cell 정보 가져오기

    transition의 헤드쿼터인 AppContentTransitionController에서 넘겨 받은 indexPath를 이용 하여 CollectionView의 cellForItem 메소드를 통해 현재 cell이 어떤 item인지 가져온다.

     

    그 후, convert를 통해 cell의 frame을 현재 view 즉, AppstoreMenuViewController의 view인 fromView에 맞게 변환하여 가져온다.

    '그냥 cell의 frame을 사용하면 되지않나?' 라고 생각할 수 있지만, collectionView는 scroll이 가능 하기 때문에 collectionView의 frame은 고정적 이지만 collectionView를 보여주고 있는 superView는 scrolling 되어 있는 상태 일수 있기 때문에 cell의 frame은 다르다.

     

    이제 frame과 indexPath가 있으니 transition에 사용 할 contentView를 만들자.

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            let containerView = transitionContext.containerView
            containerView.alpha = 1.0
            
            guard let fromVC = transitionContext.viewController(forKey: .from) as? TabBarViewController else { fatalError() }
            guard let appStoreMenuVC = fromVC.viewControllers![0] as? AppStoreMenuViewController else { fatalError() }
            guard let contentVC = transitionContext.viewController(forKey: .to) as? AppContentViewController else { fatalError() }
            guard let fromView = appStoreMenuVC.view else { fatalError() }
            guard let toView = contentVC.view else { fatalError() }
            
            let targetTabbar = fromVC.tabBar
            let fallingTabbar = UITabBar(frame: targetTabbar.frame)
            
            let targetCell = appStoreMenuVC.appCollectionView.cellForItem(at: targetIndexPath!) as! AppCollectionViewCell
            let startFrame = appStoreMenuVC.appCollectionView.convert(targetCell.frame, to: fromView)
            
            let contentView = AppContentView(isContentView: true, isTransition: true)
            
            contentView.fetchDataForContentVC(image: targetData!.image, subD: targetData!.subDescription!, desc: targetData!.description!, content: (targetData?.content)!, contentView: containerView.frame, isTransition: true)
            
            contentView.clipsToBounds = true
            contentView.layer.cornerRadius = 20
            contentView.translatesAutoresizingMaskIntoConstraints = false
            
            let vcTabBarItem = UITabBarItem(title: "투데이", image: nil, tag: 0)
            let vc2TabBarItem = UITabBarItem(title: "설정", image: nil, tag: 1)
            
            vc2TabBarItem.isEnabled = false
            
            fallingTabbar.items = [vcTabBarItem, vc2TabBarItem]
            fallingTabbar.selectedItem = fallingTabbar.items?.first
            
            toView.alpha = 0.0
            contentView.alpha = 1.0
            targetCell.alpha = 0.0
            targetTabbar.alpha = 0.0
            
            containerView.addSubview(toView)
            containerView.addSubview(contentView)
            containerView.addSubview(fallingTabbar)
    }

     

    - ContentView Layout 구성하기

     

    NSLayoutConstraint 프로퍼티를 이용하여 contentView의 layout을 잡아준다.

    이 프로퍼티들은 animation에서 사용하기 때문에 전역 프로퍼티로 만들어주자.

     

    makcConstraints 메소드를 이용하여 분기한다.

    import Foundation
    import UIKit
    
    class AppContentPresentingAnimator: NSObject, UIViewControllerAnimatedTransitioning {
        
        var contentViewTopAnchor: NSLayoutConstraint!
        var contentViewWidthAnchor: NSLayoutConstraint!
        var contentViewHeightAnchor: NSLayoutConstraint!
        var contentViewCenterXAnchor: NSLayoutConstraint!
        
        var subDescTopAnchor: NSLayoutConstraint!
        var subDescLeadingAnchor: NSLayoutConstraint!
        
        var descTopAnchor: NSLayoutConstraint!
        var descLeadingAnchor: NSLayoutConstraint!
        
        var targetIndexPath: IndexPath?
        var targetData: AppContentModel?
        
        init(indexPath: IndexPath) {
            super.init()
            targetIndexPath = indexPath
            targetData = model[indexPath.row]
        }
        
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1
        }
        
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            let containerView = transitionContext.containerView
            containerView.alpha = 1.0
            
            guard let fromVC = transitionContext.viewController(forKey: .from) as? TabBarViewController else { fatalError() }
            guard let appStoreMenuVC = fromVC.viewControllers![0] as? AppStoreMenuViewController else { fatalError() }
            guard let contentVC = transitionContext.viewController(forKey: .to) as? AppContentViewController else { fatalError() }
            guard let fromView = appStoreMenuVC.view else { fatalError() }
            guard let toView = contentVC.view else { fatalError() }
            
            let targetTabbar = fromVC.tabBar
            let fallingTabbar = UITabBar(frame: targetTabbar.frame)
            
            let targetCell = appStoreMenuVC.appCollectionView.cellForItem(at: targetIndexPath!) as! AppCollectionViewCell
            let startFrame = appStoreMenuVC.appCollectionView.convert(targetCell.frame, to: fromView)
            
            let contentView = AppContentView(isContentView: true, isTransition: true)
            
            contentView.fetchDataForContentVC(image: targetData!.image, subD: targetData!.subDescription!, desc: targetData!.description!, content: (targetData?.content)!, contentView: containerView.frame, isTransition: true)
            
            contentView.clipsToBounds = true
            contentView.layer.cornerRadius = 20
            contentView.translatesAutoresizingMaskIntoConstraints = false
            
            let vcTabBarItem = UITabBarItem(title: "투데이", image: nil, tag: 0)
            let vc2TabBarItem = UITabBarItem(title: "설정", image: nil, tag: 1)
            
            vc2TabBarItem.isEnabled = false
            
            fallingTabbar.items = [vcTabBarItem, vc2TabBarItem]
            fallingTabbar.selectedItem = fallingTabbar.items?.first
            
            toView.alpha = 0.0
            contentView.alpha = 1.0
            targetCell.alpha = 0.0
            targetTabbar.alpha = 0.0
            
            containerView.addSubview(toView)
            containerView.addSubview(contentView)
            containerView.addSubview(fallingTabbar)
            
    		targetCell.resetTransform()
            
            //MARK: - configure Layout
            
            NSLayoutConstraint.activate(makeConstraints(containerView: containerView, contentView: contentView, Originframe: startFrame))
            containerView.layoutIfNeeded()
        }
        
        func makeConstraints(containerView: UIView, contentView: AppContentView, Originframe: CGRect) -> [NSLayoutConstraint] {
            
            contentViewCenterXAnchor = contentView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor)
            contentViewTopAnchor = contentView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: Originframe.minY)
            contentViewHeightAnchor = contentView.heightAnchor.constraint(equalToConstant: Originframe.height)
            contentViewWidthAnchor = contentView.widthAnchor.constraint(equalToConstant: Originframe.width)
    
            return [contentViewCenterXAnchor, contentViewTopAnchor, contentViewHeightAnchor, contentViewWidthAnchor]
            
        }
    }
    

     

    - Animation 구현

    animation을 구현하기 위해 만들어둔 NSLayoutConstraint의 값을 바꿔 준다.

    topAnchor와 나머지 weidth와 height값을 따로 animate 시켜준다.

     

    import Foundation
    import UIKit
    
    class AppContentPresentingAnimator: NSObject, UIViewControllerAnimatedTransitioning {
        
        var contentViewTopAnchor: NSLayoutConstraint!
        var contentViewWidthAnchor: NSLayoutConstraint!
        var contentViewHeightAnchor: NSLayoutConstraint!
        var contentViewCenterXAnchor: NSLayoutConstraint!
        
        var subDescTopAnchor: NSLayoutConstraint!
        var subDescLeadingAnchor: NSLayoutConstraint!
        
        var descTopAnchor: NSLayoutConstraint!
        var descLeadingAnchor: NSLayoutConstraint!
        
        var targetIndexPath: IndexPath?
        var targetData: AppContentModel?
        
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
           
           	...
            
            //MARK:- TopConstraints Animation
            
            contentViewTopAnchor.constant = 0
            
            UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: .curveLinear, animations: {
                contentView.layoutIfNeeded()
            }) { (comp) in
                toView.alpha = 1.0
                contentView.removeFromSuperview()
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
           
            //MARK:- View's Height and Width Animation
            
            contentViewWidthAnchor.constant = containerView.frame.width
            contentViewHeightAnchor.constant = containerView.frame.height
            
            contentView.subDescriptionLabel.snp.remakeConstraints { (const) in
                const.top.equalTo(contentView.snp.top).offset(GlobalConstants.safeAreaLayoutTop)
                const.leading.equalTo(contentView.snp.leading).offset(20)
                const.width.equalTo(contentView.snp.width).multipliedBy(0.8)
            }
            contentView.descriptionLabel.snp.remakeConstraints { (const) in
                const.top.equalTo(contentView.subDescriptionLabel.snp.bottom).offset(10)
                const.leading.equalToSuperview().offset(20)
                const.width.equalTo(contentView.snp.width).multipliedBy(0.8)
            }
            
            UIView.animate(withDuration: 0.6 * 0.6) {
                fallingTabbar.frame.origin.y = fromView.frame.maxY
                containerView.layoutIfNeeded()
            }
        }
        
        func makeConstraints(containerView: UIView, contentView: AppContentView, Originframe: CGRect) -> [NSLayoutConstraint] {
            
            contentViewCenterXAnchor = contentView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor)
            contentViewTopAnchor = contentView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: Originframe.minY)
            contentViewHeightAnchor = contentView.heightAnchor.constraint(equalToConstant: Originframe.height)
            contentViewWidthAnchor = contentView.widthAnchor.constraint(equalToConstant: Originframe.width)
    
            return [contentViewCenterXAnchor, contentViewTopAnchor, contentViewHeightAnchor, contentViewWidthAnchor]
            
        }
        }
    }
    

     

     

    contentView의 topAnchor를 containerView의 topAnchor로 붙혀주는 animate 속도보다 나머지 animate 속도가 빠른 이유는 topAnchor는 damping animation을 수행해야 하기 때문이다.

     

     

    아직 Dissmissal Tranisition을 적용하기 전이므로 targetCell과 Tabbar는 사라져 있는 상태이다.

     

     

    import Foundation
    import UIKit
    
    class AppContentTransitionController: NSObject, UIViewControllerTransitioningDelegate {
        
        var superViewcontroller: UIViewController?
        var indexPath: IndexPath?
        
        func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
            return AppcontentPresentaion(presentedViewController: presented, presenting: presenting)
        }
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AppContentPresentTransitioningAnimator(indexPath: indexPath!)
        }
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return nil
        }
        func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
            return nil
        }
    }

    2번째 메소드 획득

     

     


     

    댓글

Designed by Tistory.