Search code examples
iosiphoneswiftswift4catransition

Hide a black fade when self.dismiss with CATransition


I want to make my viewController to dismiss to an old viewController with animation. But when I use CATransition my viewController and old viewController fades black. Is there any way I can make it not to fade?

let transition = CATransition()
transition.duration = 1.0
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromLeft
view.window!.layer.add(transition, forKey: kCATransition)
self.dismiss(animated: false, completion: nil)

Solution

  • Read more about transition here and try to modify available parameters to achieve required effects.

    Good undocumented info about transition available here.

    In case you want some good solution - you should prepare custom transition for your screen - check this WWDC video

    Alternative fast (but not the best) solution may be manual control of presentation/dismiss in your viewController:

    import UIKit
    
    final class AnimatableViewController: UIViewController {
    
        private var panStartLocation:CGPoint = CGPoint.zero
        private var panPrevLocation:CGPoint = CGPoint.zero
    
        var onClose:(()->())?
    
        // MARK: - LifeCycle
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            openAnimation()
        }
    
        // MARK: - IBAction
    
        @objc private func closeButtonAction(_ sender: UIButton) {
            dismiss(animated: true, completion: { [weak self] in
                self?.onClose?()
            })
        }
    
        // MARK: - Gestures
    
        @IBAction private func didDetectPan(_ sender: UIPanGestureRecognizer) {
            switch sender.state {
                case .began:
                    panStartLocation = sender.location(in: navigationController?.view)
                    panPrevLocation = panStartLocation
                case .cancelled, .ended:
                    finishAnimation()
                case .changed:
                    let newPoint:CGPoint = sender.location(in: navigationController?.view)
                    let dy:CGFloat = newPoint.y - panPrevLocation.y
                    var newPosition:CGPoint = view.center
    
                    if (newPoint.y - panStartLocation.y > 0) {
                        newPosition.y += dy
                    } else {
                        newPosition.y = self.view.bounds.height / 2
                    }
                    view.center = newPosition
                    panPrevLocation = newPoint
                default:
                    break
            }
        }
    
        //MARK: Animation
    
        private func finishAnimation() {
            let center:CGPoint = view.center
            if (center.y > self.view.bounds.height / 2 * 1.35) {
                closeAnimation()
            } else {
                openAnimation()
            }
        }
    
        private func closeAnimation() {
            let fullAnimTime:CGFloat = 0.5
            let endPoint:CGPoint = CGPoint(x:view.center.x, y:view.bounds.height / 2 * 3)
            let animTime:CGFloat = ((endPoint.y - view.center.y) / self.view.bounds.height) * fullAnimTime
    
            UIView.animate(withDuration: TimeInterval(animTime), animations: { [weak self] in
                self?.view.center = endPoint
                }, completion: { [weak self] _ in
                    self?.dismiss(animated: false, completion: {
                        self?.onClose?()
                    })
            })
        }
    
        private func openAnimation() {
            let fullAnimTime:CGFloat = 0.5
            let endPoint:CGPoint = CGPoint(x: view.center.x, y:self.view.bounds.height / 2)
            let animTime:CGFloat = ((view.center.y - endPoint.y) / (self.view.bounds.height / 2)) * fullAnimTime
            UIView.animate(withDuration: TimeInterval(animTime)) { [weak self] in
                self?.view.center = endPoint
            }
        }
    }
    
    extension AnimatableViewController: UIGestureRecognizerDelegate {
        // MARK: - UIGestureRecognizerDelegate
    
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
                return false
            }
            return true
        }
    }
    

    In storyboard don't forget to set you segue parameters as:

    enter image description here

    And add panGesture to your root view or any additional pan Gesture to top most view, that can be used for pan - in my case - i just add pan to rootView:

    enter image description here

    Result:

    enter image description here