I tried making transition animation for changing between two viewController. Is there any way to make that without using storyboard?
This code for calling new viewController (which I didn't use storyboard):
#objc func handleAccount() {
let navController = UINavigationController(rootViewController: userButtonLauncher())
navController.transitioningDelegate = self
navController.modalPresentationStyle = .custom
self.present(navController, animated: true, completion: nil)
}
And this codes for transition:
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .present
transition.startingPoint = CGPoint(x: 0, y: 0)
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .pop
transition.startingPoint = CGPoint(x: 0, y: 0)
return transition
}
Of course you can! It depends on what you would like to do, so if you need to use the defaults animation on code without using storyboard you can just:
instantiate your ViewController, set the animation you want and open the ViewController
let vc = self.storyboard?.instantiateViewController(withIdentifier: "vc_id")
vc.modalTransitionStyle = .flipHorizontal
self.present(vc, animated: true, completion: nil)
Otherwise you can create your custom transition, see this easy guide:
https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started
I solve my problem. I just add that code for calling animation transition in my homecontroller:
extension HomeController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return faceanim()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return faceanim()
}
}
And to button action is:
#objc func handleAccount() {
let userConVC = UINavigationController(rootViewController: userButtonLauncher())
userConVC.transitioningDelegate = self
navigationController?.isNavigationBarHidden = false
self.present(userConVC, animated: true, completion: nil)
}
Related
Can somebody explain why my fromViewController is nil? I am trying to understand custom transition animations in iOS going back and forth between two different view controllers.
Removing the guard part of my code and modifying it a little bit, my present animation works, but I cannot figure out how to implement the dismiss animation. Any suggestions?
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
{
let containerView = transitionContext.containerView
guard
let fromViewController = transitionContext.view(forKey: .from),
let toViewController = transitionContext.view(forKey: .to)
else
{
print("Nope")
return
}
containerView.addSubview(toViewController)
if (presentationMode == .Present)
{
toViewController.alpha = 0.0
UIView.animate(withDuration: 1.0,
animations: {
toViewController.alpha = 1.0
},
completion: {
_ in transitionContext.completeTransition(true)
})
}
Edit: This is where I use the animation
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let secondVC = segue.destination as! SignUpViewController
secondVC.transitioningDelegate = self
}
extension LoginViewController: UIViewControllerTransitioningDelegate
{
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
print("Presenting")
presentationTransition.presentationMode = .Present
return presentationTransition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
print("Dismissing")
presentationTransition.presentationMode = .Dismiss
return presentationTransition
}
}`
How can I get a presenting modal to be of custom size? Tried lots of different solutions, many which seem obsolete
This is how I instantiate the modal view from the parent view controller:
self.definesPresentationContext = true
let vc = (storyboard?.instantiateViewController(withIdentifier: "modalViewController"))!
vc.modalPresentationStyle = .overCurrentContext
vc.preferredContentSize = CGSize(width: 100, height: 100)
present(vc, animated: true, completion: nil)
But, the modal view covers the full screen instead of just occupying 100 * 100.
You need to implement UIViewControllerTransitioningDelegate methods and UIViewControllerAnimatedTransitioning methods for customizing the presented UIViewController size.
To know how to implement custom animator,
Refer to: https://github.com/pgpt10/Custom-Animator
Edit:
class ViewController: UIViewController
{
//MARK: Private Properties
fileprivate let animator = Animator()
//MARK: View Lifecycle Methods
override func viewDidLoad()
{
super.viewDidLoad()
}
override func awakeFromNib()
{
super.awakeFromNib()
self.transitioningDelegate = self
self.modalPresentationStyle = .custom
}
//MARK: Button Action Methods
#IBAction func dismissController(_ sender: UIButton)
{
self.dismiss(animated: true, completion: nil)
}
}
// MARK: - UIViewControllerTransitioningDelegate Methods
extension ViewController : UIViewControllerTransitioningDelegate
{
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
self.animator.transitionType = .zoom
self.animator.size = CGSize(width: 100, height: 100)
return self.animator
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
return self.animator
}
}
I'm trying to present a ViewController (embedded in a NavigationController) from a button inside a TableViewController. The presented ViewController should be half the height of the TableViewController. I've tried with the following code below but it doesn't seem to work (Swift 3). Can someone kindly help? thanks!
class AddNewRecipeTableViewController: UITableViewController, UIViewControllerTransitioningDelegate {
#IBAction func popUpTest(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let pvc = storyboard.instantiateViewController(withIdentifier: "popUpTest") as! UINavigationController
pvc.modalPresentationStyle = UIModalPresentationStyle.custom
pvc.transitioningDelegate = self
self.present(pvc, animated: true, completion: nil)
}
func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController!, sourceViewController source: UIViewController) -> UIPresentationController? {
return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)
}
}
class HalfSizePresentationController : UIPresentationController {
override var frameOfPresentedViewInContainerView : CGRect {
return CGRect(x: 0, y: 0, width: containerView!.bounds.width, height: containerView!.bounds.height/2)
}
}
You have:
func presentationControllerForPresentedViewController(
presented: UIViewController,
presentingViewController presenting: UIViewController!,
sourceViewController source: UIViewController)
-> UIPresentationController? {
That method will never be called, because in Swift 3 it doesn't correspond to any method that Cocoa knows about. (I'm suprised you don't report getting a warning from the compiler about this.)
You probably meant to implement presentationController(forPresented:presenting:source:), like this:
func presentationController(
forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController)
-> UIPresentationController? {
But even that won't be called, because you have not set the presented view controller's modalPresentationStyle to .custom.
I have followed this tutorial and watched the WWDC video on this subject but I couldn't find my answer.
I have almost the same transition in my code. It is working pretty well when I am doing it as a presented view, but not as a pushed view.
It is supposed to animate a snapshot of the pushed view from a CGRect to the full screen and vice versa when popped.
Here is the code of my UIViewControllerAnimatedTransitioning class:
class ZoomingTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
let originFrame: CGRect
let isDismissing: Bool
init(originFrame: CGRect, isDismissing: Bool) {
self.originFrame = originFrame
self.isDismissing = isDismissing
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return Constant.Animation.VeryShort
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else {
return
}
let finalFrame = transitionContext.finalFrame(for: toVC)
toVC.view.frame = finalFrame
let snapshot = self.isDismissing ? fromVC.view.snapshotView(afterScreenUpdates: true) : toVC.view.snapshotView(afterScreenUpdates: true)
snapshot?.frame = self.isDismissing ? finalFrame : self.originFrame
snapshot?.layer.cornerRadius = Constant.FakeButton.CornerRadius
snapshot?.layer.masksToBounds = true
containerView.addSubview(toVC.view)
containerView.addSubview(snapshot!)
if self.isDismissing {
fromVC.view.isHidden = true
} else {
toVC.view.isHidden = true
}
let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration,
animations: {
snapshot?.frame = self.isDismissing ? self.originFrame : finalFrame
},
completion: { _ in
if self.isDismissing {
fromVC.view.isHidden = false
} else {
toVC.view.isHidden = false
}
snapshot?.removeFromSuperview()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
Then, I tried to show a new View Controller using 2 ways: by presenting it and by pushing it.
My FromViewController is subclassing both UINavigationControllerDelegate and UIViewControllerTransitioningDelegate.
FromViewController class presenting the ToViewController (which works fine):
func buttonAction(_ sender: AnyObject) {
self.tappedButtonFrame = sender.frame
let toVC = self.storyboard!.instantiateViewController(withIdentifier: "ToViewController")
toVC.transitioningDelegate = self
self.present(toVC, animated: true, completion: nil)
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let transitionController = ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: false)
return transitionController
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let transitionController = ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: true)
return transitionController
}
FromViewController class pushing the ToViewController (which doesn't work):
func buttonAction(_ sender: AnyObject) {
self.tappedButtonFrame = sender.frame
let toVC = self.storyboard!.instantiateViewController(withIdentifier: "ToViewController")
self.navigationController?.pushViewController(toVC, animated: true)
}
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .push:
return ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: false)
case .pop:
return ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: true)
default:
return nil
}
}
When pushing, the delegate method is called and the ZoomingTransitionController performs its code fine (going in animateTransition until the end without notable issue). But on the screen, the snapshot view isn't display at any moment. The ToVC appears after the transition duration, but without anything else meanwhile.
I am running out of idea on how to debug this... Do you have any idea?
Thanks!!
I found my answer by replacing the snapshot element (which was causing the problem) by a CGAffineTransform of the toVC.
Code is almost the same than here.
This is my extension:
extension UIViewController: UIViewControllerTransitioningDelegate {
func presentAssignBookToClassesViewController(controller: BWAssignBookToClassesViewController) {
controller.modalPresentationStyle = .Custom
controller.transitioningDelegate = self
controller.preferredContentSize = CGSizeMake(500, 575)
presentViewController(controller, animated: true, completion: nil)
}
func presentSettingsStoryboard() {
if let settingsController = UIStoryboard(name: "TeacherSettingsStoryboard", bundle: nil).instantiateInitialViewController() {
settingsController.modalPresentationStyle = .Custom
settingsController.transitioningDelegate = self
settingsController.preferredContentSize = CGSizeMake(500, 575)
presentViewController(settingsController, animated: true, completion: nil)
}
}
//MARK: - UIViewControllerTransitioningDelegate
public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
}
Within presentationControllerForPresentedViewController: I need to return either BWOverlayPresentationController or BWSettingsPresentationController depending on what method was called. How to achieve this?
You can simply distinguish them via restorationIdentifier (you can set this simply using storyboard):
//MARK: - UIViewControllerTransitioningDelegate
public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
if presented.restorationIdentifier == BWSettingsRestorationIdentifier {
return BWSettingsPresentationController(presentedViewController: presented, presentingViewController: presenting)
} else {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
}
I would suggest you can create one BaseViewContoller with two viewController objects eg: BWOverlayPresentationController, BWSettingsPresentationController and based on condition you can return the the specific view controller.
public func presentationControllerForPresentedViewController(presented: UIViewController?, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
let viewController = BaseViewController()
if (viewController.(somePropertyInViewController)) {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
else {
return BWSettingsPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
public func presentationControllerForPresentedViewController(presented: UIViewController?, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
// You can create some property in presented/presenting viewController.
// and check here to return specific viewContoller.
if (presented.(somePropertyInViewController)) {
return BWOverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
else {
return BWSettingsPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
}