How to removeFromSuperview() with a transition

2.3k views Asked by At

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???

1

There are 1 answers

0
Andrea On BEST ANSWER

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.