iosswiftaddsubviewcatransition

How to removeFromSuperview() with a transition


I have added a view as sub view using the following code

    let controller = SideMenuViewController.instantiateViewControllerFromStoryboard(storyBoardName: "Module1") as! SideMenuViewController

    UIView.animate(withDuration:0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
    }, completion: {
        (finished: Bool) -> Void in


        controller.view.tag = 100

        let transition = CATransition()
        transition.type = kCATransitionPush
        transition.subtype = kCATransitionFromLeft
        controller.view.layer.add(transition, forKey: nil)
        self.view.addSubview(controller.view)
        self.addChildViewController(controller)
        controller.didMove(toParentViewController: self)



    })

The result is a view controller(sidemenu) sliding from left and revealing. I want to remove the added subview with a transition from right to left I have tried removing from superview with animation using the following code

UIView.animate(withDuration:0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {}, completion: {


self.view.viewWithTag(100)?.removeFromSuperview()


})

But this does not result in a smooth transition.How can I remove the added subview with a smooth transition,similiar to the way it was shown???


Solution

  • The removeFromSuperview() method is not animatable, what you can do is making alpha goes to 0 and after it has finished you can safely remove from super view.
    If you want to get the same effect as in push, you just need to take your code and create the opposite transition. Since it seems that you are using view controllers you can take advantage of the transitions API.

    func push(_ viewController: UIViewController, animated: Bool, completion: (()->())?) {
            let oldVC = viewControllersStack.topItem()!
            viewController.view.frame = oldVC.view.frame
            self.addChildViewController(viewController)
            oldVC.willMove(toParentViewController: nil)
            let duration = animated ? Constants.GeneralValues.PopPushAnimationDuration : 0.0
            transition(from: oldVC, to: viewController, duration: duration, options: [], animations: { () -> Void in
                let animation = CATransition()
                animation.duration = CFTimeInterval(duration)
                animation.type = kCATransitionMoveIn
                animation.timingFunction = CAMediaTimingFunction(name: "easeInEaseOut")
                animation.subtype = "fromRight"
                animation.fillMode = "forwards"
                self.mainContainerView.layer.add(animation, forKey: "animoteKey")
    
                // Constraint
                guard let v = viewController.view else {
                    return
                }
                v.translatesAutoresizingMaskIntoConstraints = false
                let hConstr = NSLayoutConstraint.constraints(withVisualFormat: "H:|[v]|", options:[], metrics:nil, views:["v":v])
                let vConstr = NSLayoutConstraint.constraints(withVisualFormat: "V:|[v]|", options:[], metrics:nil, views:["v":v])
                let constrs: [NSLayoutConstraint] = [hConstr, vConstr].flatMap {$0}
                NSLayoutConstraint.activate(constrs)
                }) { (finished) -> Void in
                oldVC.removeFromParentViewController()
                self.viewControllersStack.push(viewController)
                viewController.didMove(toParentViewController: self)
                if let completion = completion {
                    completion()
                }
            }
        }
    
        func popViewController(_ animated: Bool, completion: (()->())?) {
            let oldVC = viewControllersStack.topItem()!
            let viewController = viewControllersStack.penultimate!
            viewController.view.frame = oldVC.view.frame
            self.addChildViewController(viewController)
            oldVC.willMove(toParentViewController: nil)
            let duration = animated ? Constants.GeneralValues.PopPushAnimationDuration : 0.0
            transition(from: oldVC, to: viewController, duration: duration, options: [], animations: { () -> Void in
                let animation = CATransition()
                animation.duration = CFTimeInterval(duration)
                animation.type = kCATransitionReveal
                animation.timingFunction = CAMediaTimingFunction(name: "easeInEaseOut")
                animation.subtype = "fromLeft"
                animation.fillMode = "forwards"
                self.mainContainerView.layer.add(animation, forKey: "animoteKey")
                // Constraint
                guard let v = viewController.view else {
                    return
                }
                v.translatesAutoresizingMaskIntoConstraints = false
                let hConstr = NSLayoutConstraint.constraints(withVisualFormat: "H:|[v]|", options:[], metrics:nil, views:["v":v])
                let vConstr = NSLayoutConstraint.constraints(withVisualFormat: "V:|[v]|", options:[], metrics:nil, views:["v":v])
                let constrs: [NSLayoutConstraint] = [hConstr, vConstr].flatMap {$0}
                NSLayoutConstraint.activate(constrs)
    
                }) { (finished) -> Void in
                print("Fine")
                oldVC.removeFromParentViewController()
                _ = self.viewControllersStack.pop()
                viewController.didMove(toParentViewController: self)
                if let completion = completion {
                    completion()
                }
            }
        }
    

    This is a sort of implementation that I used to achieve the same you want but for pushing and popping view controllers in a container, but the animation is the same, just change constraints, to keep the sideview controller thin.