Inside my app I have a MainVC. From there I present another ViewControllerB and I would like to push inside there so this is how I achieve this:
let ViewControllerB = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerB") as! ViewControllerB
let navController = UINavigationController(rootViewController: communityVC)
self.present(navController, animated: true, completion: nil)
This is working exactly how I want it to work. However in
ViewControllerB I also would like to push a certain ViewControllerC full screen from bottom and NOT just inside the modalView. I am calling this:
let firstLaunchVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FirstLaunchVC")
let transition:CATransition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromTop
self.navigationController?.modalPresentationStyle = .fullScreen
self.navigationController!.view.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(firstLaunchVC, animated: true)
This is pushing from the bottom but NOT fullscreen. I also get this message in the terminal:
Setting modalPresentationStyle once <UINavigationController: 0x1060f2a00> has been presented will have no effect until it is dismissed and presented again.
How can I push fullscreen from a presented ViewController?
I hope I made my problem clear. Let me know if you need more information on something.
Coordinator for transition - UINavigationControllerDelegate
Create a simple class that will conform to UINavigationControllerDelegate, I won't get into details but the main method which is the answer for your question is navigationController(_:animationControllerFor:from:to:)
Inside body of this method you can retrieve the operation which is being performed (push, pop...), additionally you can make a decision for animator based upon the view controllers (from, to).
In case you want default animator just return nil.
Example
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .push: // For push method
return YourCustomAnimator() //Conforming to `UIViewControllerAnimatedTransitioning`
case .pop:
return nil // For pop we want default animation
// I skipped the default case
}
// CASE 2
// Or for example you want to make custom animation only for specific view controller
if toVC is SpecialViewController {
return YourCustomAnimator()
}
return nil // for the rest use default animation
}
I fixed it by calling this:
let navigationController = UINavigationController(rootViewController: firstLaunchVC)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true)
This is working exactly how I want it to work, even though I am not quite sure if I am messing up my ViewController-Hirarchy... anyway it is working :D thanks for the help
Related
This appears to be a well documented problem, yet the solutions online have not worked.
Here's just a sample list of posts that failed to provide me with a working answer:
ViewWillAppear not executing code
viewWillAppear not getting called
viewWillAppear not called
UINavigationController Inheritance, ViewWillAppear not called
viewWillAppear not called after popToViewController
iPhone viewWillAppear not firing
I have gleaned that my problem with viewWillAppear not getting called has to do with my view hierarchy. I am using a tab controller that is not the highest part of the view hierarchy. One of the tab controller's view controllers is a root view controller to a navigation controller. That's where I am trying to get a working viewWillAppear or viewDidAppear. Here's what I tried that has not worked. Within the tab controller I added this code:
let nav2 = UINavigationController(rootViewController: locationsVC)
nav2.beginAppearanceTransition(true, animated: false)
//...//
viewControllers = [ nav1, nav2, nav3, nav4 ]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for vc in self.children {
vc.beginAppearanceTransition(true, animated: animated)
}
}
In the scene delegate, this is my code:
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
let rootVC = NewOrExistingViewController()
rootVC.beginAppearanceTransition(true, animated: false)
let rootNC = UINavigationController(rootViewController: rootVC)
rootNC.navigationController?.navigationBar.isHidden = true
rootNC.beginAppearanceTransition(true, animated: false)
self.window?.rootViewController = rootNC
let tbc = TabBarViewController()
tbc.selectedIndex = 0
tbc.beginAppearanceTransition(true, animated: false)
rootVC.add(asChildViewController: tbc)
Try calling rootVC.add(asChildViewController: tbc) before rootVC.beginAppearanceTransition(true, animated: false).
All your child VCs need to already be children of the parent before you call func beginAppearanceTransition(_: animated:)
I have a main tabBarController and would like to present a viewController modally when a certain tabBarItem is tapped.
I am loading the viewControllers in my tabBarController as...
func setupViewControllers() {
self.tabBar.isHidden = false
if let firstVC = storyboard?.instantiateViewController(withIdentifier: "first") as? FirstViewController, let secondVC = storyboard?.instantiateViewController(withIdentifier: "second") as? SecondViewController, let thirdVC = storyboard?.instantiateViewController(withIdentifier: "third") as? ThirdViewController, let fourthVC = storyboard?.instantiateViewController(withIdentifier: "fourth") as? FourthViewController, let fifthVC = storyboard?.instantiateViewController(withIdentifier: "fifth") as? FifthViewController {
let firstNavController = UINavigationController(rootViewController: firstVC)
let secondNavController = UINavigationController(rootViewController: secondVC)
let fourthNavController = UINavigationController(rootViewController: fourthVC)
let fifthNavController = UINavigationController(rootViewController: fifthVC)
firstNavController.tabBarItem.image = image
secondNavController.tabBarItem.image = image
fourthNavController.tabBarItem.image = image
fifthNavController.tabBarItem.image = image
thirdVC.tabBarItem.image = image
tabBar.tintColor = nil
//Load tabBar viewControllers
viewControllers = [homeNavController, postNavController, plusMenuVC, meetupNavController, profileNavController]
}
}
I then conformed the tabBarViewController to the UITabBarControllerDelegate to invoke the method...
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if tabBarController.selectedIndex == 2, let thirdVC = viewController as? ThirdViewController {
thirdVC.modalPresentationStyle = .overFullScreen
thirdVC.modalTransitionStyle = .crossDissolve
present(thirdVC, animated: true, completion: nil)
return false
} else { return true }
}
The above however never gets triggered. I tried setting in viewDidLoad
self.delegate = self
I tried setting the root navigation controllers and its ancestor tabBarController delegate to self.
Nothing seemed to work and I hope someone can guide me as I've been unable to debug and find an existing solution...
UPDATE
So I created a dummyThirdVC to replace the thirdVC in the setupViewControllers() function. In the dummyThirdVC, I conformed to UITabBarControllerDelegate and in viewDidLoad I set the self.tabBarController.delegate = self. Then I took the delegate method and entered it into this dummyThirdVC, where inside this delegate method, I instantiated the real thirdVC to present.
The delegate method finally triggers properly, but my issue now is, the dummyThirdVC and its view must first load and appear for the delegate to be set and triggered thereafter.
How can I not show the dummyThirdVC and immediately just present the instantiated, real thirdVC? I had tried dummyThirdVC.viewDidLoad() in my setupViewControllers function to no avail...
I believe your check is wrong. You're checking for selectedIndex to be 2, but the selectedIndex 's value will always be the actual tab bar's selected index, and not the index that is going to be selected, so you're basically never going to reach selectedIndex as 2.
You also cannot present a view controller that is already active, and thirdVC is already active in your tabBar, thus you will get an error. A workaround for this is to use a viewController as placeholder for image and title in the tab bar and instantiate another one for presenting.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController is ThirdViewController {
let vcToPresent = storyboard?.instantiateViewController(withIdentifier: "third") as? ThirdViewController
vcToPresent.modalPresentationStyle = .overFullScreen
vcToPresent.modalTransitionStyle = .crossDissolve
present(vcToPresent, animated: true, completion: nil)
return false
}
return true
}
I have a navigation bar in my application that I change the navigation to present modally But I want when user goes to the destination view controller the navigation animation from left to right
here is my codes in the first view controller
#IBAction func showProfilePage(_ sender: UIBarButtonItem) {
self.performSegue(withIdentifier: "showprofile", sender: self)
}
I read apple site for this and use this function too
private func navigationController(_ navigationController: UINavigationController,
willShow viewController: ProfileViewController,
animated: Bool) {
}
But it doesn't work so what should I do to change navigation animation from left to right?
Try this.
let storyBoard : UIStoryboard = UIStoryboard(name: "PatientCheckout", bundle:nil)
let controller = storyBoard.instantiateViewController(withIdentifier: "PatientCheckoutViewController") as UIViewController
let transition = CATransition.init()
transition.duration = 0.45
transition.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionDefault)
transition.type = kCATransitionPush //Transition you want like Push, Reveal
transition.subtype = kCATransitionFromLeft // Direction like Left to Right, Right to Left
transition.delegate = self
view.window!.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(controller, animated: true)
I am presenting a view controller after pushing another view controller with animation as my application requires it. The code is as given below:
func pushViewController() {
let viewcontroller = UIStoryboard(name: "Home", bundle: nil).instantiateViewController(withIdentifier: "homeSearchIndentifier") as? HomeSearchViewController
let searchViewController = UIStoryboard(name: "Home", bundle: nil).instantiateViewController(withIdentifier: "searchRideIdentifier") as? SearchRideViewController
searchViewController?.parentView = .homeLandingPage
let duration = 0.4
let transition: CATransition = CATransition()
transition.duration = duration
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromBottom
self.navigationController!.view.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(viewcontroller!, animated: false)
viewcontroller?.present(searchViewController!, animated: true, completion: nil)
}
In the presented view controller i am assigning a Side Menu controller as i need to show the side menu while swiping to the right:
override func viewDidLoad() {
super.viewDidLoad()
self.addSearchListScreen()
self.locationManager.delegate = self
yourLocation.layer.sublayerTransform = CATransform3DMakeTranslation(7, 0, 0)
let leftViewController = UIStoryboard(name: "SideBar", bundle: nil).instantiateViewController(withIdentifier: "sideBar") as? SideBarViewController
let slideMenuController = SlideMenuTrackerController(mainViewController: self, leftMenuViewController: leftViewController!)
AppDelegate.sharedInstance().window?.rootViewController = slideMenuController
leftViewController?.mainViewController = self
slideMenuController.automaticallyAdjustsScrollViewInsets = true
AppDelegate.sharedInstance().window?.backgroundColor = UIColor(red: 236.0, green: 238.0, blue: 241.0, alpha: 1.0)
AppDelegate.sharedInstance().window?.makeKeyAndVisible()
}
Now i have a custom back button in this view controller. On clicking it i need to dismiss the view controller and show the view controller from where it was presented . The name is "HomeSearchViewController"
For dismissing i wrote the following code in the button action:
func navigateToPreviousController() {
self.dismiss(animated: true, completion: nil)
}
But this is not dismissing the view controller. May i know what is the issue?
in order to dismiss the view controller your view controller should be embedded inside a navigation controller.
eg:
let controller =storyboard.instantiateViewController(withIdentifier: "storyboardidentifier") as! yourviewcontroller
let navController = UINavigationController(rootViewController: controller)
present(navController, animated: true, completion: nil)
and then on the presented view controller if you use below code it will dismiss the view controller.
func dismissButtonPressed(){
self.dismiss(animated: true, completion: nil)
}
Cheers!!
Edit: My first answer addressed only part of the problem.
Looking further at your code...
It appears you are pushing HomeSearchViewController onto the navigationController stack, and then also presenting SearchRideViewController from HomeSearchViewController.
However...
In:
HomeSearchViewController viewDidLoad()
you create an instance of
SideBarViewController (leftViewController)
and you create an instance of
SlideMenuTrackerController (slideMenuController)
and assign
self as the .mainViewController and
leftViewController as the .leftMenuViewController
of slideMenuController.
If that's not confusing enough, you also:
AppDelegate.sharedInstance().window?.rootViewController = slideMenuController
which replaces HomeSearchViewController ... which you had just pushed onto the navigationController stack!
So, your view hierarchy is now (or, at least, seems to be if I am following your code):
Window
slideMenuController (instance of SlideMenuTrackerController)
+- searchViewController (instance of SearchRideViewController)
+- leftViewController (instance of SideBarViewController)
In order to "get back to" where you came from, you will need to re-create whatever VC you want to get to and (again) replace AppDelegate.sharedInstance().window?.rootViewController.
Without seeing your entire project, I'm not sure how you should really go about that.
[original answer]
In here:
func pushViewController() {
// ... the other lines
self.navigationController?.pushViewController(viewcontroller!, animated: false)
viewcontroller?.present(searchViewController!, animated: true, completion: nil)
}
You push viewcontroller and also .present(searchViewController!... from viewcontroller (that's a bit odd to begin with, but regardless)...
Then in here:
func navigateToPreviousController() {
self.dismiss(animated: true, completion: nil)
}
You .dismiss a view controller, but you never .pop back on the navigation controller's stack.
I am trying to imitate the behaviour that can be observed with SFSafariViewController. When presented, the push transition animation is used just like with a root UINavigationalController. When doing an edge pan swipe right, the pop transition follows.
let url = URL(string: "http://google.com")!
let safari = SFSafariViewController(url: url)
present(safari, animated: true, completion: nil)
However, I need the same transition to work with any other UIViewController.
let storyboard = UIStoryboard(name: "Place", bundle: nil)
let controller = storyboard.instantiateInitialViewController()
present(controller!, animated: true, completion: nil)
Using CATransition you can able to change the animation direction while presenting the ViewController
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
view.window!.layer.add(transition, forKey: kCATransition)
present(controller, animated: false, completion: nil)
#Fuxing, It is not possible to push a view controller without navigation controller.Ther are two option to push a view controller.
Add a navigation controller to your viewController in the storyboard.
Create an object of the navigation controller.
let viewToPush = YourViewController()
let nav = UINavigationController(rootViewController: viewToPush)
nav.pushViewController(nav, animated: true)