I am new to swift and I am curious about the best way to move from one view to another. I have a main view that is a tab on a tab bar and embedded in a navigation controller. From this main view, I am taking user content and then displaying that content on the main view. Currently I am segueing from the main view to a view that collects content and then another segue back to the main view. The only problem is that when I do this I get a back button and even if I hide the back button, I am just continuing to put screens on top of the tab. What would be the best way to transition from the view that collects content back to the main view?
Basically there're 2 general ways how view controllers are shown and hidden:
Push in the navigation controller (either with 'Show' segue or in code self.navigationController?.pushViewController(viewController, animated: true). In that case the normal way to return to previous screen would be to use 'Back' navigation button. If you need to do it manually you can use self.navigationController?.popViewController(animated: true) inside your view controller or use unwind segue (though I rarely see it used). If you need to come back to the very first screen in your navigation stack you can use self.navigationController?.popToRootViewController(animated: false).
Modal presentation (either with 'Present Modally' segue or with self.present(viewController, animated: false). In that case in order to return to the previous view you have to add some close/cancel button on your screen and bind it with the code which call self.dismiss(animated: true, completion: nil).
Anyway to have a full picture I'd recommend to get familiar with Apple guide on this subject.
And if you need to pass some data between your controllers, ther're various ways to do it, like assigning it directly to a property in prepare(for segue: UIStoryboardSegue, sender: Any?), implementing delegate/callback interactions, implementing observers, shared back storages, etc.
Related
I have a basic scenario:
I present a VC modally using self.present(, animated:, completion:).
Sometimes due to interactions in this modal VC i need to close one modal and open another one.
So i do the following:
weak var presenter = self.presentingViewController
let newVc = UIViewController()
presenter?.dismiss(animated: true, completion: {
presenter?.present(newVc, animated: true, completion: nil)
})
This works but there is the annoying delay when switching the VC's when user sees the original presenter and can try to interact with it (to open other modals...).
I tried setting animated: false but that doesn't seem to work :/
I can't really switch to UINavigationController model for this because the modals i am presenting themselves are Page View Controllers and have the whole hierarchy of dependent views; the user is never going 'back'; so i'd really like to just present the new modal as quickly as possible...
Update My question is not about how to control or choose the animation. My questions is about having no delay between the modals.
The built-in view controller architecture that switches views with no transition is the tab bar controller. So just turn your view controller into a tab bar controller — with no visible tab bar! To change to the other view controller just change tabs (in code). The change is instant.
This screencast makes it clear that this works as described. We present a view controller (yellow). Then we switch back and forth between two view controllers (green and yellow) as the presented view controllers, instantly. Finally, we dismiss whichever one (green or yellow) is showing. I'm doing it all with simple buttons but that's just for the demo; obviously you could do this however you like. It's the architecture that's the important thing.
I can think only of solutions which would require you to handle the animations yourself
create custom modal transition using UIViewControllerTransitionCoordinator
add your controllers to container views as suggested by #muhammed-gül
present newVC over self and dismiss all presented controllers when you're done
And just a tip, you don't always need to wait for the dismiss completion closure, you can call this and it usually works, but still the underlying viewController is visible.
dismiss(animated: true)
present(newVC, animated: true)
I would like to preface this by saying I am new to Swift and IOS development.
I currently am attempting to perform a segue from a tab bar view back to the root view controller which is a split view controller.
From the picture below I would like to select the 'cancel' button and return to my root view controller which is a split view controller
Below is the storyboard segue I set up
Below is the code I am using.
#IBAction func cancelButton(_ sender: Any) {
performSegue(withIdentifier: "submit", sender: nil)
}
When I initially launch my app and begin using it. Everything works correctly.
As shown below when an item is selected from the left side the correct data populates in the detail view.
Even the side push menu works correctly when first opened as seen below.
However, it is when I select the 'Submit Mail' option in the side menu and attempt to return to the rootview with a segue that the app starts to break down.
As shown below when I return with the segue I have set up the data no longer populates in the detail view when selecting an item from the master view.
Furthermore, the side push menu is now full screen when opened.
Any reason as to why this is happening?
As a further note I am using the Cocoapod SideMenu for the side push menu.
Thanks!
Segues are used to push controllers onto the navigation hierarchy, not to go backwards, as you are attempting to do. You should think of your navigation hierarchy as a tree structure, where there are rarely any cyclic relationships between controllers. You add and remove controllers from the hierarchy to go 'forwards' and 'backwards'. What actually happens in your scenario is that a new instance of your root controller is getting pushed onto the navigation stack, one that has not been initialised with the state that your original root controller had. Although it is possible to use a special kind of segue called unwind segues to go 'backwards', I would not recommend that, since they are complicated to wire, and make for code that is not explicit about what it does.
Instead you could use UIViewController.dismiss in combination with the delegate pattern to achieve what you want.
To return from the tabBarController, I would just set an unwind segue:
declare the unwind in the root view controller (no code needed in the func)
#IBAction func unwindToRootViewController(_ sender: UIStoryboardSegue) {
// You can print("I returned"), just to test
}
Connect the button that will trigger return to the exit button of tabBarController and select unwindToRootViewController
i'm doing an app that uses a TableViewController with a system of Adding/Editing items, using the same view. (however when you add an item the view is modal presented and when you edit it is shown)
I followed the great starter tutorial from Apple so basically my Storyboard looks like this
The segue going through the Navigation Controller is for adding and the other one is for editing. (I did everything according to the tutorial).
I did a segue between the Cancel item bar button and the exit icon of the ViewController and it works well when the view is modally presented (when I try to add an item).
However when I click on a cell to reach the view with the segue that shows it (to edit an item), both items in the navigation bar stop working. The prepareForSegue method is not called anymore. So I can't cancel or save.
I tried creating an unwind segue between the ViewController itself and the exit icon and to call it programmatically like this:
#IBAction func testButton(sender: UIBarButtonItem) {
print("we're inside")
self.performSegueWithIdentifier("cancelSegue", sender: self)
print("so what now")
}
and when I try to edit it and tapping the cancel button it results by just showing the two log messages and kind of skipping the performSegueWithIdentifier method. However adding still works fine.
Am I doing something wrong or have I misunderstood some basic notion about unwind segues?
This seems like quite a strange solution to your problem. While I can't comment on specifically why your unwind segue isn't working in the second case, things will start to get quite complicated with dismissing the New Programsegue since you're displaying it two different ways.
The common approach that we use is:
Use two completely different view controllers for creating and updating an assets. May be some duplicate code, but makes it slightly easier for other people to work on.
Use the same view controller for creating and updating buttons. If you're editing an item, you can pass it to the view controller with prepareForSegue. When the view controller loads, if an item is present, you can change the behaviour of the buttons, title etc. If no item is present, you know to create a new item.
For most implementations, it's much simpler to dismiss the views programatically. (i.e [self dismissViewControllerAnimated:NO completion:nil] for modal views and [self.navigationController popViewControllerAnimated:YES] for just returning to a vc in the same navigation controller stack.
Update
Did you link the Cancel button to the exit icon on the first view controller, or the second view controller (the one with the cancel and save button)?
I'm a swift newbie and I've got a simple question which I hope someone can help me figure out.
I have a multi-tab app. I created some segues from the tab view controllers on the Stopryboard. I've given the segues identifiers and I'm calling them from my Tab1ViewController code using performSegueWithIdentifier("tab1ToMyTarget", sender: sender) no problem.
However, I'd like to be able to call the segue from any of the app's tabs without creating new segues from the other tabs' view controllers (i.e. I don't want to create "tab2ToMyTarget" - I presume there's a better way!).
My question: Do I create these 'universal' segues on the tab bar view controller (e.g. "tabBarToTarget") (and if so how do I call it from one of my tab view controllers)? ...or...
Do I keep the segue from a single tab view controller (tab1ToTarget) and call that segue from a sibling tab view controller somehow?
First, set the view controller that you want to go to's storyboard Id. Then run this:
let vc = storyboard!.instantiateViewControllerWithIdentifier("someViewController")
self.presentViewController(vc, animated: true, completion: nil)
You cannot use the same segue to open a controller from a different one. It's better to instantiate a view controller with its storyboard id and then present/show based on the flow.
I am designing two pages, one followed by the other, in Xcode 7. Let's call them the first and second view controllers. From a button in the first view controller, with a modal segue, the second view controller shows up. I want to add a button to second view controller so that when I hit that button, I can simply exit from the second and back to the first view controller.
I know this feature can be done by navigation controller with putting cancel button to the navigation bar on second view controller which I don't want to. I want specifically a button to have that functionality.
I thought of calling the first view controller with code when the button is tapped, but it sounds me like a bad coding. So, I would like to learn what is a good way of achieving this.
Thanks,
Add method to the second view controller:
#IBAction func exitButtonPressed(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil);
}
Next add button to the second view controller in interface builder and connect button's action to this method.
use Unwind Segues
Unwind Segues give you a way to “unwind” the navigation stack and specify a destination to go back to.
for sample tutorial1, tutorial2