I came across a library call Motion on GitHub which provide sets of transition on view and controllers.
Based on the website, to use the animation on navigation controller we need to create a new navigation controller class like below, and I apply the new navigation controller. But when it failed and not pushing to the new view controller.
the GitHub link is https://github.com/CosmicMind/Motion
class AppNavigationController: UINavigationController {
open override func viewDidLoad() {
super.viewDidLoad()
isMotionEnabled = true
motionNavigationTransitionType = .zoom
}
}
then on the main view:
let navigation = AppNavigationController()
navigation.pushViewController(newViewController, animated: false)
If you're using Motion's animated NavigationController throughout your app, you can set it this way in your AppDelegate's didFinishLaunchingWithOptions method.
let newViewController = NewViewController(nibName: "NewViewController",bundle: nil)
let navigation = AppNavigationController(rootViewController: newViewController!)
self.window?.rootViewController = navigation
self.window?.makeKeyAndVisible()
UPDATE:
If you want to create a separate NavigationController and push specific UIViewController to it, then you need to present the navigation controller instead of pushing it. See below.
let motionNavController = AppNavigationController(rootViewController: galleryViewController)
motionNavController.motionNavigationTransitionType = .none
motionNavController.isNavigationBarHidden = true
self.present(motionNavController, animated: true, completion: nil)
Related
I am using this code
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
self.navigationController!.pushViewController(controller, animated: true)
and also added:
self.tabBarController?.tabBar.isHidden = false
If I understand your hierarchy correctly you have a tab bar controller which has navigation controllers in it. So basically any of the tabs can push additional view controllers and tab bar is still visible.
Now you want to push some new controller on the currently selected view controller in the tab bar and you want to do it from another part of the app, another view controller that has no relation to tab bar.
The quickest way to do that is to expose a static instance of your tab bar view controller. This will only work if you always have only 1 tab bar controller in your application (probably 99% of the applications).
First add a current instance to your tab bar view controller:
class MyTabBarViewController: UITabBarController {
static private(set) var currentInstance: MyTabBarViewController?
override func viewDidLoad() {
super.viewDidLoad()
MyTabBarViewController.currentInstance = self
}
}
So when view loads a static value is assigned and can now be accessed anywhere in your project via MyTabBarViewController.currentInstance.
The rest is then just accessing the currently selected view controller and pushing a new view controller. Something like this should do:
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
(MyTabBarViewController.currentInstance?.selectedViewController as? UINavigationController)?.pushViewController(controller, animated: true)
You must push in TabBarController
self.tabBarController?.pushViewController(controller, animated: true)
I have in my project tow view controller and I linked the first VC with navigation controller but the problem is : I used present to go to second VC (that mean I didn't use segue) ... how can I set back to first VC in navigation Controller by code (without segue) .
picture from my storyboard
my code :
let storyboard = self.storyboard
let viewcontroller = storyboard?.instantiateViewController(withIdentifier: "contact_detail") as! ViewController2
viewcontroller.arr2 = arr
present(viewcontroller, animated: true, completion: nil)
With the dismiss method it will work.
Dismisses the view controller that was presented modally by the view controller. (Apple Docs)
self.dismiss(animated: true, completion: nil)
If you are presenting a viewcontroller with present method, you can dismiss it with dismiss method.
If you are adding any view controller with push method then only it will get added to your navigation stack and you can remove it by calling popviewcontroller on it's back action.
Here, you are presenting a viewcontroller, hence it will not get added to your first navigation stack and you can not remove it with pop action on click of back.
If you are looking for a back button like feature on presented view controller, you can add a back button in toolbar, dismiss a viewcontroller on back button action, and can animate it like a popnavigation while dismissing.
extension UINavigationController {
public func removeViewController(classes : [String]) {
var vcs = [UIViewControllers]()
for viewController in self.viewControllers {
let name = viewController.className
if !classes.contains(name) {
vcs.append(viewController)
}
}
if classes.count < vcs.count {
self.viewControllers = vcs
}
}
}
now think you have 4 viewControllers , A, B, C, D, you want to remove B and C and Move Back To A
In D's View Controller
override func viewDidLoad() {
super.viewDidLoad()
//your works
let viewControllersToRemove = [String(describing: type(of:B)), String(describing: type(of:C))]
navigationController.removeViewControoler(classes : viewControllersToRemove)
}
I solved this by using pushViewController
self.navigationController?.pushViewController(MyViewController, animated: true)
I have a UILabel in my ViewController that has a NavigationController (let's say view controller A) with a tap gesture recognizer attached to the label. When the label is tapped another view appears (let's call it B). The user picks some text in B and the view dismisses back to A with the label text updated with the selection. So I created a delegation between A and B to get the selection. The problem is that I do not see the NavigationBar when B appears. Is there a way to fix this?
ViewController A
#IBOutlet weak var sectionName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let sectionLabelTap = UITapGestureRecognizer(target: self, action: #selector(labelTapped(_:)))
sectionName.isUserInteractionEnabled = true
sectionName.addGestureRecognizer(sectionLabelTap)
}
#objc func labelTapped(_ sender: UITapGestureRecognizer) {
let sectionNameVC = storyboard?.instantiateViewController(withIdentifier: "SectionName") as! SectionNameTableViewController
sectionNameVC.selectionNameDelegate = self
sectionNameVC.userData = userData
present(sectionNameVC, animated: true, completion: nil)
}
In order to display the Navigation bar the UIViewController needs to have a UINavigationController.
You can add that sectionNameVC ViewController into a UINavigationController to persevere the present animation.
In that case your code might look something like this:
#objc func labelTapped(_ sender: UITapGestureRecognizer) {
let sectionNameVC = storyboard?.instantiateViewController(withIdentifier: "SectionName") as! SectionNameTableViewController
sectionNameVC.selectionNameDelegate = self
sectionNameVC.userData = userData
let naviagtionController = UINavigationController(rootViewController: sectionNameVC)
present(naviagtionController, animated: true, completion: nil)
}
Or you can simply call pushViewController on the View Controller A's navigation Controller, like this:
self.navigationController?.pushViewController(sectionNameVC, animated: true)
This will add sectionNameVC into the View Controller A's navigation Controller stack. In this case the transition animation will be different, the sectionNameVC will come from your right.
You are missing the concept between "Presenting" View Controller & "Navigating" the View Controller. You will get the answer, once you understood the concept. Here, it is..
When you are presenting the ViewController, you are completely replacing the stack container to the new view controller.
STACK holds the addresses of the ViewControllers you push or pop via navigating.
e.g:
present(sectionNameVC, animated: true, completion: nil)
On the other hand, if you are navigating to other view controller by pushing it. In this case, you can go back to previous controller by simple popping the ViewController address from stack.
e.g:
self.navigationController?.pushViewController(sectionNameVC, animated: true)
self.navigationController?.popViewController(animated: true)
So, If you navigate then, only you will get navigation Bar.
Now, in your case, you are presenting the ViewController and hence, navigation bar is not showing.
So I'm having a table view controller and using https://github.com/jonkykong/SideMenu i'm trying to display a "slide in" sidebar which works, but it doesn't show me the view that I want in the sidebar is black
// Define the menus
let menuLeftNavigationController = UISideMenuNavigationController()
menuLeftNavigationController.leftSide = true
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration of it here like setting its viewControllers.
SideMenuManager.menuLeftNavigationController = menuLeftNavigationController
// Enable gestures. The left and/or right menus must be set up above for these to work.
// Note that these continue to work on the Navigation Controller independent of the View Controller it displays!
SideMenuManager.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
SideMenuManager.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
When It clicks on the sidebar button I have this, which creates the animation but doesn't show the viewcontroller
func someAction(){
present(SideMenuManager.menuLeftNavigationController!, animated: true, completion: nil)
debugPrint("clicked")
}
On the repo readme, you will find your answer, take a look on the Customization
section https://github.com/jonkykong/SideMenu#sidemenumanager.
Simply set menuFadeStatusbar = false
SideMenuManager.default.menuFadeStatusBar = false
From the previous link, you will find the following information : "Draws the menuAnimationBackgroundColor behind the status bar. Default is true.
If menuFadeStatusBar is true, this color is used to fade it. Default is black."
The answer is in the comment of the snippet you posted:
// UISideMenuNavigationController is a subclass of UINavigationController,
// so do any additional configuration of it here like setting its viewControllers.
let btnMenu:UIButton = UIButton()
btnMenu.frame = CGRect(x: 20*valuePro, y: 20*valuePro, width: 40*valuePro, height: 40*valuePro)
btnMenu.backgroundColor = .red
btnMenu.addTarget(self, action: #selector(self.displayMenu), for: .touchUpInside)
self.view.addSubview(btnMenu)
#objc func displayMenu(sender: UIButton!) {
print("Button Clicked")
present(SideMenuManager.default.menuLeftNavigationController!, animated: true, completion: nil)
}
https://github.com/jonkykong/SideMenu
The SideMenu README
Code Implementation
First:
import SideMenu
From a button, do something like this:
// Define the menu
let menu = UISideMenuNavigationController(rootViewController: YourViewController)
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration
// of it here like setting its viewControllers. If you're using storyboards, you'll want to do something like:
// let menu = storyboard!.instantiateViewController(withIdentifier: "RightMenu") as! UISideMenuNavigationController
present(menu, animated: true, completion: nil)
Answer
You need use UISideMenuNavigationController(rootViewController:), not UISideMenuNavigationController().
If you open the side menu from left, set leftSide to true.
let menu = UISideMenuNavigationController(rootViewController: YourViewController)
menu.leftSide = true
present(menu, animated: true, completion: nil)
Of course, you should give instance variable to UISideMenuNavigationController(rootViewController:).
I can recommend using JASidePanels
It's pretty simple and it just works. You're creating an JASidePanelController, setting this class to empty viewcontroller in your Storyboard and making this controller initial. (do not forget to import JASidePanels at the top of the class)
Then, in this class, you're implementing awakeFromNib() method like this:
leftPanel = ..//instantiating menu controller
let centerController = ...//instantiating center controller
centerPanel = UINavigationController(rootViewController: centerController)
That's it.
You can instantiate controller via their ID which can be set in Identity Inspector
let stb = UIStoryboard(name: "Main", bundle: nil) //instantiating a storyboard we will use for instantiating controllers
let someController = stb.instantiateViewController(withIdentifier: "here_your_identifier_goes") as! YourControllerClass
I have a tab bar in my app and one of the buttons is "Profile" which should present one VC if user not authorized and another if user already authorized.
So... Things work, but not correctly. Looks like I messed up some root controllers. Sometimes I cant pop up vc or change the color of a nav bar title.
I create a custom navigation controller with that code inside
if NSUserDefaults.standardUserDefaults().objectForKey("userId") != nil {
let newController: UserViewController = self.storyboard?.instantiateViewControllerWithIdentifier("userViewController") as! UserViewController
self.setViewControllers([newController], animated: false)
} else {
let newController: LoginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("loginViewController") as! LoginViewController
self.setViewControllers([newController], animated: false)
}
Is there a way to implement in correctly?
You're trying to instantiate a Navigation Controller - which (as #PierreMB had mentioned) won't work. You should instead create two regular View Controllers with storyboard ID's "userViewController" and "loginViewController", and instantiate them.
Create a UINavigationController subclass, and put this code (which you wrote, slightly modified) in its viewWillAppear() method (as a bonus, you can pass the 'animated' parameter free of charge):
if NSUserDefaults.standardUserDefaults().objectForKey("userId") != nil {
let newController = self.storyboard?.instantiateViewControllerWithIdentifier("userViewController") // drop the 'as! UINavigationController'
self.setViewControllers([newController], animated: animated)
} else {
let newController = self.storyboard?.instantiateViewControllerWithIdentifier("loginViewController") // drop the 'as! UINavigationController'
self.setViewControllers([newController], animated: animated)
}
The function instantiateViewControllerWithIdentifier() by default returns a UIViewController, which is what you really want to display.
Are you trying to put a UINavigationController as a root of another UINavigationController ? Because it is prohibited and doesn't work well.