I have a UITabBarController which is connected to 3 UINavigationControllers. Each of these have a UIViewController as a root view controller. When I click on a button in one of these ViewControllers (VC1), I want it to segue to new ViewController (VC2). Simple enough.
During runtime, when the segue takes place and VC2 appears, the navigation bar disappears.
The navigation bar is present in the storyBoard / interface builder, and the segue is the type: present (Push).
Code in VC1:
#IBAction func create_clicked(_ sender: Any) {
performSegue(withIdentifier: "segueIdentifier", sender: self)
}
It might help to know how the UITabBarController tabBar is instantiated:
func login() {
let storyBoard = UIStoryBoard(name: "Main", bundle: nil)
let tabBar = storyBoard.instantiateViewController(withIdentifier: "tabBar")
window?.rootViewController = tabBar
}
Please help.
After a great deal of experimentation, we determined that even though Interface Builder said this was a Show (Push) segue, and even though we were in a Navigation Controller to start with,
it was behaving as a Present Modally segue.
So we changed the segue type, using the pop-up menu in Interface Builder, from Show (Push) to plain Push, even though that's deprecated — and the interface worked correctly.
And then we changed it back to Show (Push) and the interface continued to work correctly. Problem solved!
I suspect the storyboard was corrupted in some way. Obviously Interface Builder should not lie to you about what kind of segue this is, but basically, that is what it seems to have been doing.
EDIT The problem was caused, apparently, by making a Show Detail segue and changing it to Show. When you do that, it's still a Show Detail segue, which in this context behaves as a modal presentation. That's an Xcode bug! I reported it, and Apple now says this will be fixed in Xcode 9.3.
Related
Well, I'm faced with misunderstanding.
I want my UITabBar to stay displayed when I make a segue from my History button (see picture).
My segue is on Show.
My view is embed in a UINavigationController that is root controller at my UITabBarController.
"Show navigation bar" is turned off on my UINavigationController.
And with all that, I have a grey screen on my segue and no UITabBar...
Thank you in advance!
Try creating a popup view.
In the storyboard remember to set the identifier for the view controller that you are going to. Then delete the segue.
let customAlert = self.storyboard?.instantiateViewController(withIdentifier: "view") as! yourViewController
self.addChild(customAlert)
self.view.addSubview(customAlert.view)
customAlert.view.backgroundColor = UIColor.white
Please make Segue kind as "Show". When select as "Show Detail", the ViewController is present over the Tabbar and hides it.
I have a viewcontroller embedded in a navigationcontroller that pushes another viewcontroller onto the stack. This pushed viewcontroller has an embedded viewcontroller that segues/modally presents a final viewcontroller.
On a button click, I am trying to dismiss the final presented viewcontroller and pop the present-ing viewcontroller and return to the initial state.
Thus far, I've been able to get the dismiss going, but popping does not seem to work in the completion handler of the dismiss.
I've tried printing out the hierarchy, i.e. self.presentingViewController, self.navigationController, self.presentingViewController.presentingViewController..., all of which output nil, and am admittedly stuck now on returning to the initial state.
In looking at the view hierarchy, the final presented viewcontroller is beneath a UITransitionView separate from the rest of the stack I had mentioned earlier..
Any thoughts/guidance would be appreciated.
Since you mentioned segues I think unwind segues might help. I built a quick test project and they do indeed function correctly in your scenario.
There is a rather excellent answer in a related SO question What are Unwind segues for and how do you use them?. A summary of the answer for your particular case is: place the following function in your initial view controller:
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue)
{
}
You can then directly 'unwind' to that viewcontroller by using Storyboard Segues directly (as in the referenced answer) or programatically via:
self.performSegue(withIdentifier: "unwindToThisViewController", sender: self)
Again there's a good article entitled Working with Unwind Segues Programmatically in Swift which goes into lots of detail.
Can you try
if let nav = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
self.dismiss(animated:true) {
nav.popToRootViewController(animated:true)
}
}
When presenting or dismissing VC, I do not want to keep hiding and showing tabBar because it creates a poor user experience. Instead, I want present the next VC straight over the tab bar such that when I dismiss the nextVC by dragging slowly from left to right, I can see the tabBar hidden behind the view (As shown in image below)
Note, my app has two tabs with two VCs(VCA,VCB) associated to it. Both VC also have navigation bar embedded. VCA segues to VCA1 and VCB segues to VCB1. At the moment, inside VCA and VCB I am calling the following function to segue with some hiding and unhiding done when viewWillappear (Code below).
self.navigationController?.showViewController(vc, sender: self)
// Inside ViewWillAppear Only reappear the tab bar if we successfully enter Discover VC (To prevent drag back half way causing tab bar to cause comment entry to be floating). This code check if we have successfully enters DiscoverVC
if let tc = transitionCoordinator() {
if tc.initiallyInteractive() == true {
tc.notifyWhenInteractionEndsUsingBlock({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
if context.isCancelled() {
// do nothing!
}
else {
// not cancelled, do it
self.tabbarController.tabBar.hidden = false
}
})
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
----------Working solution from GOKUL-----------
Gokul's answer is close to spot on. I have played with his solution and came up with the following improvement to eliminate the need to have a redundant VC and also eliminate the initial VC being shown for a brief second before tabVC appears. But without Gokul, I would never ever come up with this!!
Additionally, Gokul's method would create a bug for me because even though I do have a initial "normal" VC as LoginVC before tabVC is shown. This loginVC is ONLY the rootVC if the user needs to login. So by setting the rootVC to tabVC in most cases, the navVC will never be registered.
The solution is to embed navigation controller and tabBar controller to one VC. But it ONLY works if the navVC is before the TabBarVC. I am not sure why but the only way that allowed me to have navVC-> tabVC-> VC1/VC2 is to embed VC1 with a navVC first than click on VC1 again to embed tabVC (It wouldn't allow me to insert one before tabVC and I also had to click the VC1 again after embedding the NavVC).
For your requirement we need to make some small changes in your given view hierarchy
Let me explain step by step,
To meet your requirement we have to add a UIViewController(let's say InitialVC) embedded with a UINavigationController and make it as initial viewcontroller.
Then add a UITabbarController with 2 VC (VCA,VCB) // IMPORTANT: Without any navigationcontroller embedded.
Add a segue between InitalVC and TabbarController with an unique identifier(ex: Initial)
In viewWillAppear of InitalVC perform segue as below (InitialVC is unnecessary to our design we are using this just to bridge navigationController and tabbarController).
self.performSegueWithIdentifier("Initial", sender: nil)
In TabbarControllerclass hide your back button, this ensures that InitialVC is unreachable.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
}
Now add a segue from a button between VCA and VCA1, thats it build and run you will see VCA1 presenting over VCA's tabbar.
What we have changed?
Instead of adding UINavigationController inside UITabbarController we have done vice versa. We can't directly add Tabbar inside navigation to do that we are using InitialVC between them.
Result:
1st way is create a image of the tabbar using UIGraphicsGetImageFromCurrentImageContext and set it on the bottom of the other view...
2nd way is show the next view in another new window that is above the tabbar, that way you wont need to hide the tabbar anymore, but seems like its in the navigation controller so this way doesnt seems available
Hiding and unhiding the tab bar is unnecessary. You only need to embed the UITabBarController inside the UINavigationController. That is, UINavigationController as the initial vc, UITabBarController as the root vc of UINavigationController.
I have implemented SWRevealViewController into my project successfully. The side menu is a UITableView with different cells which navigate to differing view controllers. When I tap on the 'profile' cell it defaults to the LoginViewController, there the user will login in and, once verified, will go to the ProfileViewController. This leads to my problem, when I navigate to the ProfileViewController the Navigation bar button (on the left) does not work properly. Likewise, when I press the logout button (the right navigation bar button) it does navigate back to the LoginViewController, however, the menu button does not work.
My navigation functions are as follows:
func switchVC() { //Navigates to the profileViewController
let storyboard = UIStoryboard.init(name: "Login", bundle: nil)
let nav = storyboard.instantiateViewControllerWithIdentifier("Profile")
UIApplication.sharedApplication().keyWindow?.rootViewController = nav
}
func switchBack() { //Navigates back to the LoginViewController
let storyboard = UIStoryboard.init(name: "Login", bundle: nil)
let nav = storyboard.instantiateViewControllerWithIdentifier("Login")
UIApplication.sharedApplication().keyWindow?.rootViewController = nav
}
My storyboard is as follows:
Any help that you may be able to provide would be greatly appreciated. Thank you everyone!
Cheers
Your question is somewhat difficult to answer, but maybe this will help. In order for SWReveal View Controller to work, you always have to route to the main SWReveal Controller Scene for the menu button to work. For example, if my storyboard looked like
SWRevealViewController Scene --> Navigation Controller Scene --> Main View
and I routed back to the Navigation Controller Scene, the button would not work. You have to route back to the first Instance of the SWRevealController Scene that comes before the navigation / any other scenes you are trying to reach.
If you need to add another instance of SWRevealViewController Scene to make that work, just segue through the sw_front identifier to the navigation controller/ view controller you need the button to work in, and then segue it through the sw_rear identifier back to the same one instance of the menu.
Sorry if this answer is confusing, please let me know if I can clarify anything.
Im using StoryBoard and i am not so sure how to instantiate a ViewController or how to reference one.
The thing is i have two view controllers and i have one with a button. I want to go to the other view controller when i pressed the button from the first view controller.
I have tried something like that:
let secondViewController:UIViewController = UIViewController()
self.presentViewController(secondViewController, animated: true, completion: nil)
Does anyone knows how to explain it to me? Thanks!
There are couple of ways of navigating between view controllers. Here's how you do it in code without segues if you're going that way.
Presenting a view controller modally.
Say you have 2 view controller scenes in the storyboard, FirstViewController and SecondViewController. You want to transit from FirstViewController to SecondViewController and there is no segue between them.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewControllerWithIdentifier("SecondVC") as UIViewController
presentViewController(secondViewController, animated: true, completion: nil)
The important part is the that you have to assign an identifier to the view controller you want to go. In this case its the SecondViewController. The way you assign an identifier is you select the view controller, open up the right panel and in it, go to the Identity Inspector (the third one from the left) and under the Identity, assign an identifier to the Storyboard ID field. I put mine as SecondVC as you can see from the code snippet above.
Push to another view controller.
If you want to push on to another view controller instead of presenting it, all you have to do is embed the FirstViewController in a UINavigationController and change the code to this.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewControllerWithIdentifier("SecondVC") as UIViewController
navigationController?.pushViewController(secondViewController, animated: true)
In a comment of yours I saw you want to go to the next view controller based on a condition. Well all you have to do is check your condition in a if else statement and execute either of those above code.
Using Segues.
If you're going to use segues instead of code, here's how you do it.
In the storyboard first you select the button and Ctrl drag to the other view controller. You'll be prompted to choose between different segues. Select show for push or show detail for modal transition (I've explained what these are below). And that's it! If you run it and tap the button, you'd be taken to the other view controller.
But if you want more control over this, you have to do a little more work.. Instead of creating a segue directly from the button, select your view controller and select that little yellow icon on top (This is in Xcode 6. In older Xcode versions its under the view controller scene). Ctrl drag from that icon to the other view controller you want to transit to. You can see a connection appears between those two controllers. Select the segue and open up the right panel and go to the Attributes Inspector (The forth one from the left). Give a name to the field identifier. I gave ToSecond.
Now create a normal action from the button. And you have to call a method called performSegueWithIdentifier passing that identifier. What it does is basically execute a segue with the identifier we give.
#IBAction func segueButtonPressed(sender: UIButton) {
performSegueWithIdentifier("ToSecond", sender: nil)
}
And this would work. You can do the conditions checking here inside a if else and if the conditions are met, call performSegueWithIdentifier.
One other thing you're likely to face is having multiple buttons in a view controller and segueing to different view controller when you tap each of them. A method called prepareForSegue fires each time a segue happens. And inside it, you can check for current segue identifier and execute it. The below code snippet will make this clearer.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ToSecond" {
let secondViewController = segue.destinationViewController as SecondViewController
}
}
Another thing I'd like to mention is that presentViewController and pushViewController are deprecated from iOS8. Instead you can use showDetailViewController for modal and showViewController to push.
showDetailViewController(secondViewController, sender: nil)
navigationController?.showViewController(secondViewController, sender: nil)
I'm not sure if these are backwards compatible. Meaning using show and show detail will work if you're developing for iOS 7 as well. If you are then just stick with the older methods.
First set seques of uistoryboardviewcontroller and try this
self.performSegueWithIdentifier("push", sender: self)
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "push" {
}
In the storyboard, select VC1 and then select the button and press control and while holding down the left button or touchpad drag across to your VC2. Then a menu should pop up. Select modal.
Run and Test. It should now perform a transition.
To transition back, the easiest way is to embed VC1 in a Navigation Controller. To do this, zoom out, select VC1 and go to the top of the screen and select:
Editor > Embed > Navigation View Controller.
Now test and run. You should have the option to go back.
If you are binding view controller programmatically you need to follow this step if you are creating storyboard based application.
It is similar what we don in Objective-c just the syntax has changed.
According to your question what you need to do is go to Main.storyboard and need to select identity inspector.
There you will be able to view identity which contains two fields
1.) Storyboard ID
2.) Restoration ID
Give them the name of view controller you have binded with class in storyboard id and select check box below restoration id. It will automatically copy storyboard ID in restoration ID.
You need to do same for all your view controllers.
let secondView = self.storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.presentViewController(secondView, animated: true, completion: nil)
you need to write the name you have entered in Storyboard ID for self.storyboard?.instantiateViewControllerWithIdentifier("/* Storyboard ID */")
You can also visit the link:
Instantiate and Present a viewController in Swift
okay,
in interface builder control click on your button and drag the blue line that appears to the second view controller. The second view controller will highlight blue. You can release and the button is connected. In the popup menu select "modal segue". No code necessary. XCODE handles it.
Watch this video for a demo.