I struggle on the concept of an app. There is basically a ViewController for data input. The base ViewController has an embedded NavigationController to provide a ToolBar. The Toolbar is supposed to have three buttons to open other ViewControllers presenting the same input data differently.
My question now is what concept do I need to use to be able to start each ViewController (1-3) from the base ViewController and to make it possible to switch between the ViewControllers at the last Hierarchy Level.
I know how to call ViewControllers but how do I implement an additional Toolbar in ViewController 1-3 for switching between the VC 1-3 (another Navigation Controller?)? And how do I make sure that VC 1-3 are not dismissed and reopend again when switching between VC 1-3? How would all open VCs (1-3) closed (dismissed) once the base ViewController is called again by the user?
I try to find some helpful answers in the Apple doc but I failed so far.
Thanks!
Related
I intend to build a rather complicated app and I am wondering how to design it correctly.
I have a rootcontroller that is in charge of dispatching the creation of several Controllers (VC1 to VC5), each of them associated with its own storyboard.
So each of these storyboards are quite complex with several levels of controllers usually driven by a navigationcontroller.
My question is the following : let assume that the user is using the deepest controller in the storyboard of VC1. In this controller there is a button. When the user click it I want VC1 and all the controllers linked to it to close and also to have some data sent to the rootcontroller.
My idea is alse to be able to reuse VC1 to VC5 somewhere else in the app but from a different controller than rootcontroller.
What is the best design to achieve that ?
Thks
First off, there is no best design.There might be better design in this world for everything.
Your First Quest:
You can use Tabbar Controller with 5 Tabs.
Each tab will have navigation controllers for holding your complex design.
For data passing:
Unwind Segue.
Delegates.
Notifications.
For your quest :
user click it I want VC1 and all the controllers linked to it to close and also to have some data sent to the rootController.
There are many tutorials of how to go back to root view controller along with data.One of them is passing-data-between-view-controllers
I don't want to elaborate this whole procedure but the theme is.
One of many solutions:
Assign that data to destination VC' variable before moving to that VC .For navigation controller, to move to rootView controller,
[self.navigationController popToRootViewControllerAnimated:YES];
is used. Hope i've given some light. :)
Let's say we have a CustomNavigationController that subclass UINavigationController.
Let's use the following example to explain the question.
CustomNavC -> pushes on VC A. From VC A you can push on 2 different VC's. VC B and VC C. We'll say both these VC's push on various other VC's, further down the rabbit hole.
Now, let's say we want to show a UIView that acts as a banner view appearing directly underneath the navigation controller. However, we only want the banner to show on say VC A, VC C, VC E, VC J, etc.
Is there any possible way to do this from the CustomNavC itself? Or is the only way to gain this control of which VC's show the banner... is to put it on the VC's itself?
1) We put it on the CustomNavC view. When the user moves from a VC to a VC that both show the banner (A->C), we want to same banner to remain. We gain this by laying out the banner on the CustomNavC. However, how can we check whether a VC should be displaying the banner or not? Every time the NavC pushes or pops a VC, we would have to check. Likely some function on each VC like -(BOOL)allowBannerViewDisplay and VC's can opt in.
2) If we put the BannerView on individual VC's, it becomes a bit easier but the deal breaker is that if we move from VC A -> VC C, the user is going to see 2 separate banners during the transition instead of the same banner.
So, we need to solve it way 1. The CustomNavC listens for a notification and displays the banner. It would then need to check the currently displaying VC and only display the banner if the VC allowed it. However, if the user transitions to another VC, it needs to recheck the logic of whether the banner is currently displaying and if it is, check if that VC wants it to display.
All of this feels weird to me.
Suggestions?
I've never implemented something like this, but off the bat my thought is this:
Set up this custom view as a new class, and pass the reference along your VCs. Set up a method on the custom View as a class method that takes in a UIViewController and returns a bool for whether or not it should be displayed (rather than on each of your VCs). This class method can get the class and see if it exists in an array of class names or something.
Now, I've never moved a UIView from one VC to another, but I think it's possible to remove the UIView as a subview from the VC it's on, and place it on the new one as a subview.
Alternatively, perhaps you could have a datasource/delegate object for this custom view, and create a new custom UIView on each VC as needed that all reference the same datasource / delegate to set up the state appropriately.
I am running into a problem here. I am presenting views with performSegueWithIdentifier. Everything goes smoothly. The problem is that as a test, I only have 2 viewControllers with some data in them and I have two buttons that call a segue back to the other VC. If I keep performingSegues, you can clearly see that the memory usage goes up every two segues by around 0.4Mb. This tells me that the Views are not being deleted/removed from the view stack and are just using memory. I would like to know the correct way of getting rid of the view that presents the other view by using performSegueWithIdentifier (of course, after it finished presenting the view, else it will not work I guess).
Could you point me in the right direction? I found some objective-c code that tried to do this but it was very extensive and I don't know much about objective-C, so it was a little hard for me to understand.
Thank you in advance for your help!
Cheers!
Edit:
I am doing a "manual segue". By this I mean I have two view controllers standing on their own. They are not embedded in any navigationVCs or something like that. I am using an "Adaptive Segue" of type "Show".
If your goal is to always keep one VC, and since you already have a manual segue, what you can do is, after presenting the next VC you can pop the existing VC from the root and assign the destination VC as your root VC like so
class ManualSegue: UIStoryboardSegue {
override func perform() {
sourceViewController.presentViewController(destinationViewController, animated: true) {
self.sourceViewController.navigationController?.popToRootViewControllerAnimated(false)
UIApplication.sharedApplication().delegate?.window??.rootViewController = self.destinationViewController
}
}
}
When you do normal segues, you are piling up new view controllers on top of the existing ones without dismissing them. That's why your memory usage keeps going up.
You need to provide more information before we can give you specific guidance. Are you doing modal segues or pushing onto a navigation stack?
If you're pushing, you want to pop to the view controller you came from. Think in terms of a stack of plates. When you push, you add plates to the stack. When you pop, you take plates off and reveal the plates (view controllers) underneath. Do a search in the Xcode help system on "PopTo" to find the methods that let you either pop back to a specific view controller or back to the root view controller of your navigation controller.
Modal view controllers stack on top of each other in a similar fashion, but without the origination of a navigation controller. If you've pushed a series of one or more modals and you send the dismissViewControllerAnimated:completion: method to the view controller you want get back to it will dismiss the modals that are on top of it.
You can also look at using unwind segues. Unwind segues let you go back where you came. Do a search in the Xcode help system on "Using unwind segues" for more information. Most of the tutorials on unwind segues are written in Objective-C but you should be able to find some in Swift if you look. Explaining unwind segues in enough detail to be useful is more than I can cover in this answer.
As someone who usually used separate xibs in the past I thought I'd give storyboard a go as it seemed a lot simpler to use and much easier to develop with. I've been writing an application where the essential set up is this:
At the top of all this is a UINavigationController (first level). Then I have Multiple UIViewControllers (second level) with buttons in them which you can tap to switch between the second level UIViewControllers.
However a problem occurs when I start switching between the second level UIViewControllers. I first thought this was an initialisation problem with the NSMutableArrays because in my code I have a NSTimer set to loop periodically and found when I set a breakpoint during it, when I went forward to the next timer tick event there appeared to be different instances of the same NSMutableArrays and it seemed a gamble to try and insert new values into these array with it sometimes working, sometimes not (as it may or may not insert into the correct instance).
Then, looking at the memory usage under Debug Navigator I found the issue. Each time I "switched" between the UIViewControllers a new UIViewController was being initiated, along with all new variables.
The code I am using to switch between them is
-(void) perform {
[[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:NO];
}
Or essentially a push segue transition. This also explains why when I tried to switch back to my view, the data on that view was lost as it is a complete new view.
Does anyone know how to switch between multiple ones of these UIViewControllers (say 5) essentially like a UITabViewController would except without the tab bar being present?
First option you can do this: You can use a tabbarcontroller for switching viewcontroller and hidden the tabbar. Then on buttonclick setthe tabbar index.
Second option you can do this: Create one more view controller and in this viewcontroller subview the all switching viewController and when you want to switch viewcontroller just bring that viewcontroller view to front by delegate.
Do you need the navigation bar and other features provided by your top level navigation controller?
If not, you could use a UIPageViewController instead.
You set up all your second level view controllers and then just have to tell the page view controller which one to display.
If you implement the associated delegate methods, it will automatically provide swipe gestures to switch between them and nice animations to get them on and off screen.
You can also get it to put a UIPageControl at the bottom showing a dot for each VC with the dot for the current VC highlighted.
I have a fairly simple app thats a game for small children. There is a main screen and 5 separate levels. 3 of the 5 levels are made up of more than one VC where actions take place in the first VC in that row then code calls a modal segue to the next one in the line and so on till it reaches the end of the row and a modal segue is called linking back to the main screen. The levels that have only one VC just perform actions then segue back to the main VC.
Every segue in the app in modal.
Also every page (VC) has a home button that will segue to the main page if pressed
I set this all up in the StoryBoard and visually everything works as Id expect but when adding sound I realized that there seems to be a major problem.
If I now understand correctly (and maybe I dont) modal segues dont actually replace the current VC with the newly requested one but rather slide the newly requested one over top the original and make it the visible display.
Currently I go from main to level 1. Level 1 does some stuff and plays some sounds that repeat via a timer. If I segue back to main visually everything is fine except the sounds being played by the timers in level 1 VC continue to play and xCdoe give me the following error quite a few times
2013-01-21 22:16:07.901 TTBetaDev[678:c07] Warning: Attempt to present <MainMenuViewController: 0x7e02f40> on <BonusViewController: 0x7ecbfa0> whose view is not in the window hierarchy!
Below is a screenshot of my storyboard in case I havent explained the layout well enough.
How should this be set up to allow the navigation I would like? A what steps will I need to take to apply that to the what I already have built in the storyboards? Or will I have to re-do all my storyborad work?
I tried apples VC documentation but I couldnt understand what relates to what Im trying to do.
COuld someone please help explain this to me
You have segues going forwards AND backwards. You shouldn't do this.
e.g. Look and Main and VC 2.
You have a segue going from Main to VC 2. This means that Main will present VC 2 as a modal view controller.
When Main does this though it is still on the stack underneath VC2.
Then you have a segue from VC2 to Main. This means that VC2 will create a new Main and present it modally too. If you continue using the app you will have multiple instances of main and all the other VCs and memory consumption will rocket.
What you need to do is delete ALL the segues that go backwards. (i.e. like the one from VC2 to Main)
Then when you want to get back to main from VC2 you have to dismiss VC2.
i.e.
in Main...
//present VC2
[self performSegueWithIdentifier:#"VC2Segue" sender:nil];
//dismiss VC2
[self dismissViewController:vc2ViewControllerInstance];
or in VC2...
//dismiss VC2 from itself
[self dismissViewController:self];
The main thing though is that you can't use segues to go backwards.
TL:DR
Nothing should segue INTO Main. Any segues that go into the left hand side of main should be deleted and dealt with properly.