Custom NavigationBar in swift - ios

I am very much new to swift and ios so i need my doubts to be cleared for the below concept
I have created a view with a button having background image of back arrow and on button action i am going back to the previous controller which is the Login, is it safe to do it this way instead of using NavigationController??
#IBAction func onBackPressed(sender: AnyObject) {
let aaa : Login =
self.storyboard?.instantiateViewControllerWithIdentifier("Login")
as! Login
self.presentViewController(aaa, animated: true, completion: nil)
}
Thanks

Navigation controller is best when you want to push to next View Controller.
(i.e) Navigation Controller ->View Controller ->View Controller[for Push].
So my suggestion is if you want to come back to previous view controller use Navigation Controller
When you want to present a new View Controller that time no need.

Related

How to segue from tab bar view controller to another view which is a child of a navigation controller

I laid out my home screen (Summary screen) as shown on the screenshot below:
I added the "+" button on the UITabBarController subclass. This button will segue to a view controller and show it modally. To give you a bit of context on how I've structured my storyboards, please refer to the screenshot below:
This modal view controller has a form that the user needs to fill in, and once they're done, there's a 'Done' button which will 1) dismiss the modal, 2) take them back to the root view which is either the 'Summary' tab or the 'Details' tab then 3) take them to a list view showing the most recent data they've entered on a list (destination view – highlighted in red).
Now, doing the segue when the user taps the "+" button is simple enough. With the following code:"
menuButton.addTarget(self, action: #selector(menuButtonAction(sender:)), for: .touchUpInside)
then
func addNewExpense(action: UIAlertAction){
self.performSegue(withIdentifier: "segue_formModal", sender: self)
}
On the modal I've setup a protocol which will then send the form data back to the tab view controller.
extension XtabBarViewController: NewInfoDelegate{
func newInfoSubmitted(formData: FormData) {
performSegue(withIdentifier: "segue_ListingPage", sender: self)
}
}
But it seems that the perform segue is not working. It says the segue does not exist. "segue_ListingPage" is that segue that connects the Summary view and the destination view.
How can I segue from the tab bar view controller to the destination view? Any help is appreciated.
Thanks in advance!
You can just make the navigation view controller the entry point.
I figured it out on my own. For the benefit of those who run into the same issue here's my solution.
I simply added the code from the tab bar view controller the code:
let childVC = children[0].children[0]
childVC.performSegue(withIdentifier: "segue_TXListPage", sender: self)
So what I did here is call the performSegue from the view controller that segues to the destination controller.

Dismiss view controller not executing when using material design dialogs in iOS Swift

I'm using material design dialog for my iOS app written with swift. Here is the brief documentation of material design dialogs: https://material.io/develop/ios/components/dialogs/
I have a dialog which has 1 action and in the completion block of the action, I want to dismiss the view controller and go back to the previous view controller. The problem is that dismissing the view controller doesn't work. All instructions which are written in the completion block, such as printing something, execute except for dismissing view controller.
Here is my code :
DispatchQueue.main.async {
let alertStr = "Alert"
let alertController = MDCAlertController(title: "Error", message: alertStr)
let action = MDCAlertAction(title:"GoBack") { (action) in
self.dismiss(animated: false, completion: nil)
}
alertController.addAction(action)
self.present(alertController, animated:true, completion:nil)
}
I'd appreciate if you could help me figure out the problem.
Thanks in advance !
A couple of thoughts:
The dismiss(animated:completion:) “Dismisses the view controller that was presented modally by the view controller.” It’s not intended to dismiss the the view controller referenced by self.
Admittedly, dismiss will, “If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.” But you can’t rely upon that within the UIAlertAction for the button, because you don’t know when the dismissal of the MDCAlertController and when the action of the button is performed.
Are you sure you presented view controller and that it’s not, for example, having instead pushed on a navigation controller?
A good way of getting back to a prior view controller is an unwind segue (or see TN2298). That eliminates all ambiguity about “push” v “present” and whether dismiss will dismiss a presented view controller and instead pass the message to the presenting view controller.
have you tried to
performSegue(withIdentifier: "ViewControllerSegue", sender: nil)
you need to select your viewController on the top-bar the yellow square(name is what you predefine)
right-click and drag to the next view controller ---> Present Modally
then select the arrow and go to attributes inspector and name the identifier.

Navigation bar won't show after pushing a view controller

I'm trying to move from a View Controller to another.
I worte this function to use when the user tap on a button to move to the new view controller:
#objc private func infoButtonTap(){
let navVC = UINavigationController()
navVC.addChild(AboutViewController())
self.navigationController?.pushViewController(AboutViewController(), animated: true)
}
The problem is that the new view controller is presented on the screen but I don't have a navigation bar and a back button to move back.
I do not use Storyboard as I want to learn coding the UI.
I tried few things I found here on Stackoverflow but none worked for me.
How can I set the new view controller to have a navigation bar with back button?
UINavigationController has a variable isNavigationBarHidden
#objc private func infoButtonTap(){
let navVC = UINavigationController()
navVC.addChild(AboutViewController())
self.navigationController?.isNavigationBarHidden = false
self.navigationController?.pushViewController(AboutViewController(), animated: true)
}
You need to push the view Controller. Try this
let aboutVC = AboutVC()
self.navigationController?.pushViewController(aboutVC, animated: true)
You don't need to write any code.
Select the Root Navigation Controller the will control the app. In the Inspector Bar, select Simulated Metrics ( The Third Selection from the Right in the Inspector) and Check the Box " Is Initial View Controller". Then connect the next View controller which will be in essence the Landing page for the app. Once you connect other View Controllers to that View controller via a button for instance ( Select the button, then press Control Key + Drag to View Controller, select Show) , you will see the navigation Bar with "Back" displayed. Once that's done, you can add other view controllers and connect them from the landing page view controller and the Navigation Bars will be displayed.
For navigation to be visible and of use in an app , first you need to set up a Navigation controller with a Root view controller i.e your first controller and from there you can use push method on your navigation controller object to push a controller on to the stack.
For eg
let navVC = UINavigationController.init(rootViewController: YourFirstViewControllerObject())
navVC.pushViewController(NewViewControllerObj(), animated: true)

Swift - How to dismiss all of view controllers to go back to root

I want a my app can go to a first view controller when every time users want it.
So I want to create a function to dismiss all the view controllers, regardless of whether it is pushed in navigation controllers or presented modally or opened anything methods.
I tried various ways, but I failed to dismiss all the view controllers certainly.
Is there an easy way?
Try This :
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
it should dismiss all view controllers above the root view controller.
If that doesn't work than you can manually do that by running a while loop like this.
func dismissViewControllers() {
guard let vc = self.presentingViewController else { return }
while (vc.presentingViewController != nil) {
vc.dismiss(animated: true, completion: nil)
}
}
It would dismiss all viewControllers until it has a presentingController.
Edit : if you want to dismiss/pop pushed ViewControllers you can use
self.navigationController?.popToRootViewController(animated: true)
Hope it helps.
If you are using Navigation you can use first one
or if you are presenting modally you can second one:
For Navigation
self.navigationController?.popToRootViewController(animated: true)
For Presenting modally
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
Hello everyone here is the answer for Swift-4.
To go back to root view controller, you can simply call a line of code and your work will be done.
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
And if you have the splash screen and after that the login screen and you want to go to login screen you can simply append presentedviewcontroller in the above code.
self.view.window?.rootViewController?.presentedViewController!.dismiss(animated: true, completion: nil)
Simply ask your rootViewController to dismiss any ViewController if presenting.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.window?.rootViewController?.dismiss(animated: true, completion: nil)
(appDelegate.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true)
}
The strategy to go back to your initial view controller could vary depending on your view controllers are stacked.
There could be multiple scenarios and depending on your situation, you can decide which approach is the best.
Scenario 1
Navigation controller is set as the root view controller
Navigation controller sets View Controller A as the root
Navigation controller pushes View Controller B
Navigation controller pushes View Controller C
This is a straightforward scenario where navigationController?.popToRootViewController(animated:true) is going to work from any view controller and return you back to View Controller A
Scenario 2
Navigation controller is set as the root view controller
Navigation controller sets View Controller A as the root
View Controller A presents View Controller B
View Controller B presents View Controller C
This scenario can be solved by the answers above
self?.view.window?.rootViewController.dismiss(animated: true) and will bring you back to View Controller A
Scenario 3
Navigation controller 1 is set as the root view controller
Navigation controller 1 sets View Controller A as the root
Navigation controller 1 pushes View Controller B
View Controller B presents Navigation Controller 2
Navigation Controller 2 sets View Controller D as the root
Navigation controller 2 pushes View Controller E
Now imagine that you need to go from View Controller E all the way back to A
Using the 2 answers above will not solve your problem this time as popping to root cannot happen if the navigation controller is not on the screen.
You might try to add timers and listeners for dismissing of view controllers and then popping which can work, I think there was an answer like this above with a function dismissPopAllViewViewControllers - I notice this leads to unusual behavior and with this warning Unbalanced calls to begin/end appearance transitions for
I believe what you can do to solve such scenarios is to
start by presenting your modal views controllers from the navigation controller itself
now you have better control to do what you want
So I would change the above to this architecture first:
Navigation controller 1 is set as the root view controller (same)
Navigation controller 1 sets View Controller A as the root (same)
Navigation controller 1 pushes View Controller B (same)
Navigation controller 1 presents Navigation Controller 2 (change)
Navigation Controller 2 sets View Controller D as the root (same)
Navigation controller 2 pushes View Controller E (same)
Now from View Controller E, if you add this:
let rootViewController = self?.view.window?.rootViewController as? UINavigationController
rootViewController?.setViewControllers([rootViewController!.viewControllers.first!],
animated: false)
rootViewController?.dismiss(animated: true, completion: nil)
you will be transported all the way back to View Controller A without any warnings
You can adjust this based on your requirements but this is the concept on how you can reset a complex view controller hierarchy.
Use this code for dismiss presented viewcontrollers and pop to navigation rootviewcontroller swift 4
// MARK:- Dismiss and Pop ViewControllers
func dismissPopAllViewViewControllers() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.window?.rootViewController?.dismiss(animated: true, completion: nil)
(appDelegate.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true)
}
}
Swift 5.4:
self.navigationController?.popToRootViewController(animated: true)
Pops all the view controllers on the stack except the root view controller and updates the display.
func popToRootViewController(animated: Bool)
But if you want to go to specific controller just use the below function.
func popToViewController(UIViewController, animated: Bool)
Pops view controllers until the specified view controller is at the top of the navigation stack.
To achieve what you want, modify your navigation stack, then do popViewController.
let allControllers = NSMutableArray(array: navigationController!.viewControllers)
let vcCount = allControllers.count
for _ in 0 ..< vcCount - 2 {
allControllers.removeObject(at: 1)
}
// now, allControllers[0] is root VC, allControllers[1] is presently displayed VC. write back to nav stack
navigationController!.setViewControllers(allControllers as [AnyObject] as! [UIViewController], animated: false)
// then pop root VC
navigationController!.popViewController(animated: true)
See this for the way to further manipulate the navigation stack. If your topmost VC is modal, dismiss it first before the code above.
Create an Unwind Segue (You can find it at https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html copyright of Apple Inc.)
Unwind segues let you dismiss view controllers that have been
presented. You create unwind segues in Interface Builder by linking a
button or other suitable object to the Exit object of the current view
controller. When the user taps the button or interacts with the
appropriate object, UIKit searches the view controller hierarchy for
an object capable of handling the unwind segue. It then dismisses the
current view controller and any intermediate view controllers to
reveal the target of the unwind segue.
To create an unwind segue
Choose the view controller that should appear onscreen at the end of an unwind segue.
Define an unwind action method on the view controller you chose.
The Swift syntax for this method is as follows:
#IBAction func myUnwindAction(unwindSegue: UIStoryboardSegue)
The Objective-C syntax for this method is as follows:
- (IBAction)myUnwindAction:(UIStoryboardSegue*)unwindSegue
3. Navigate to the view controller that initiates the unwind action.
Control-click the button (or other object) that should initiate the unwind segue. This element should be in the view controller you want to dismiss.
Drag to the Exit object at the top of the view controller scene.
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/segue_unwind_linking_2x.png
Select your unwind action method from the relationship panel.
You must define an unwind action method in one of your view controllers before trying to create the corresponding unwind segue in Interface Builder. The presence of that method is required and tells Interface Builder that there is a valid target for the unwind segue.
In case anyone looking for an Objective-C implementation of the question's answer,
[self.view.window.rootViewController dismissViewControllerAnimated:true completion:nil];
func dismiss_all(view: UIView){
view.window!.rootViewController?.dismiss(animated: true, completion: nil)
}
May be what you are looking for is unwind segue.
Unwind segues give you a way to "unwind" the navigation stack back
through push, modal, popover, and other types of segues. You use
unwind segues to "go back" one or more steps in your navigation
hierarchy.
Link to documentation:
https://developer.apple.com/library/archive/technotes/tn2298/_index.html
The best and prefered way to do this is to create an unwind segue. Just follow this documentation https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html. It can de done in code or through the interface builder.

Dismiss Current View controller and open new view controller - ios Swift

I have three view controllers. Now I am in second view controller. I want to dismiss my current view controller and open third view controller. How can I do it? I can dismiss third view controller as well as second view controller by using the following code.
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(false, completion: nil)
My problem here is when I use this function screen flickering. That means the second view controller showing and dismissed. So how can I do it?
I want to dissmiss the currentview cotroller and open new view
controller
It is possible in Android. So I hope it is possible in ios too. How can I achieve this. Please some one help me.
Edit 1:
I try to dissmiss the current view controller and open new view controller using the following code. Then my second and third both are closed.
self.dismissViewControllerAnimated(false) {
// go back to MainMenuView as the eyes of the user
presentingViewController.dismissViewControllerAnimated(false, completion: nil)
}
I found the solution
I change all the view controller to the child of navigation controller. And remove the history from the third view controller like the following
let tempVCA = self.navigationController!.viewControllers
for tempVC: UIViewController in tempVCA {
if (tempVC is SECONDVC) {
tempVC.removeFromParentViewController()
}
}
I am using this piece of code from the Third view controller's viewdidload func

Resources