Xcode is very laggy with lots of View Controllers - ios

My project contains over 60 View Controllers and Xcode is very laggy when loading the storyboard.
How can I resolve this without switching to xibs? I am using Xcode 5.1 and iOS 7.x

Problem
Putting all of your ViewControllers in one Storyboard will significantly slow down Xcode (which renders XML to show your ViewControllers) if the number of the ViewControllers is above 10. Storyboard is not the place to put all of your views, because as its name suggests a board of a specific story.
Solution
Make multiple Storyboards with 5 or 6 ViewControllers each, where you can avoid rendering hell of multiple ViewControllers. You can divide your Application on different boards named by group functionality like LoginStoryboard, UserProfileStoryboard, etc.
You can instantiate a storyboard and present a specific controller by the code below:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Your Storyboard Name" bundle:nil];
UIViewController *viewController = [storyBoard instantiateViewControllerWithIdentifier:#"Your Controller ID"];
[self presentViewController:viewController animated:true completion:nil];

Keep views in more than one storyboard. It's not the problem if not every controllers are connected with segues. There is no good way to keep a large number of views in a storyboard, in addition to working on a better computer.

Related

Scenes - Use dedicated Storyboards depending iPad or iPhone

Apple now wants us to use "scenes" rather than windows and screens to display content in for iPad and iPhone. Now having added the support for scenes I seem to have lost the ability to target iPad or iPhone with Storyboards?
I set my scenes inside plist like this:
This was copied from a new project, as Apple seems to have forgotten to document how to add scenes to an existing app. Another example of Apple not documenting sufficiently!
Now I seem to have lost the ability to use different storyboards for iPad from iPhone.
Whilst I could use the same storyboard for the iPad that I use with the iPhone my app looks better with the dedicated interface I have for the iPad because I use the extra real estate it offers to give a better end user experience. iPhone is fine, the interface is best suited to a small display but looks barren on an iPad.
Help!
Now I seem to have lost the ability to use different storyboards for iPad from iPhone
It's quite simple (and, as you say, not documented). You need two completely separate scene manifest entries in your Info.plist, i.e. UIApplicationSceneManifest and UIApplicationSceneManifest~ipad. They just specify different UISceneStoryboardFile values, and you're all set just as before scenes came along.
There may be a better way to do this, but searching I couldn't see anybody else covering this. Therefore, I'm giving my solution which has been accepted by the App Store here for others to review, use, improve upon, and most importantly, not waste any time on a goose chase looking for a solution that may not exist!
The way I got around this was to add another storyboard and view controller just to handle the decision if it's a iPhone or iPad, and then immediately load up the targeted storyboard. I called this "entry decision". The only issue was when closing the subsequent views you have to ensure that you're doing it correctly or you'll end up back at the "entry decision". And with a blank view, your end user could be stuck. Therefore, to ensure a user can never really be stuck I put buttons in that view so they can manually navigate should it show up if there's a change by Apple further down the line that we don't know about. Best to cover it now.
Step 1, create an new storyboard, "entry decision":
Because I'm a nice person I explain to the user it's an error and apologise. I also give them two buttons, iPad and iPhone. In theory, if all goes well, the end user will never see this, but at no cost to us it's better to cover this possibility.
Step 2, as soon as the view controller is loading get it to move to the right storyboards we actually want.
-(void)viewDidLoad {
// Need to decide which storyboard is suitable for this particular method.
// Loading from class: https://stackoverflow.com/questions/9896406/how-can-i-load-storyboard-programmatically-from-class
if ( [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad )
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
ViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"myViewController"];
[self.navigationController pushViewController:detailViewController animated:YES];
}
else
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"myViewController"];
[self.navigationController pushViewController:detailViewController animated:YES];
}
}
- (IBAction)pickediPhone:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"myViewController"];
[self.navigationController pushViewController:detailViewController animated:YES];
}
- (IBAction)pickediPad:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
ViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"myViewController"];
[self.navigationController pushViewController:detailViewController animated:YES];
}
There's nothing really special about this as it is a mix of detecting which device and then loading the right storyboards. And for full disclosure, I include a reference back to the original code I used for loading those storyboards.
The additional two methods are just the button presses, shown here for completeness.
Step 3, update the scene configuration inside plist to target Entry Decision instead of Main.
You're targeting a different storyboard, hence this needs updated.
Step 4, returning back to your main screen.
In my app, I have a welcome screen, and then table views or other views depending on which it is. Some types work find without any issues, but as I'm using a navigational controller I need to return correctly. Therefore, this one works best for the final "back" command to the main screen:
[self.navigationController popViewControllerAnimated:YES];
Other options either went back to the Entry Decision (the fault we covered just in case) or did nothing:
[self.navigationController popToRootViewControllerAnimated:YES];
[self dismissViewControllerAnimated:YES completion:NULL];
And that is how I'm able to use my existing storyboards, which have been tweaked perfectly for iPhone and iPad over the years of this app's life, and still be able to support Apple's desire to move to scenes. My app needed to use scenes for another purpose, therefore I had no option but to go this route. I went through many attempts to try and trap the scenes as they're getting loaded, but the initial scene seems to get loaded without you, the programmer, getting any options in how it should be or which one to use. You need this pre-view controller to give you that functionality back, and it is one solution which is active in the App Store now.
Be nice.

Navigate between multiple storyboards in iOS App

I wanted to have separate storyboard files for every UIViewcontrollers in my iOS app.
So how can we assign different storyboards for each controllers? Also how do we do navigation between those?
This I am doing to avoid svn conflicts while so many people working on UI.
get a reference to the storyboard...
UIStoryboard *someStoryboard = [UIStoryboard storyboardWithName:#"NameOfYourStoryboard" bundle:nil];
then instantiate either the initial viewcontroller from that storyboard...
UIViewController *initialViewController = [someStoryboard instantiateInitialViewController];
or some other viewcontroller identified by it's storyboard identifier...
UIViewController *someOtherViewControllerFromTheStoryboard = [someStoryboard instantiateViewControllerWithIdentifier:#"SomeViewControllersStoryboardIdentifier"];
after that you can simply push (within a navigation controller) or present the new viewcontroller.
since iOS 9.0 you can even connect storyboards via storyboard references in the storyboard itself:
https://developer.apple.com/library/prerelease/ios/recipes/xcode_help-IB_storyboard/Chapters/AddSBReference.html
Keeping different storyboards for different modules is good approach. You can achieve the navigation between storyboards as follows:-
Suppose you are in A view controller and want to push another view controller named FabIdeaDetailViewController which is present in storyboard named FabIdeas:-
FabIdeaDetailViewController *horizontalListController = (FabIdeaDetailViewController*)[UIViewController instantiateViewControllerWithIdentifier:#"FabIdeaDetailViewController" fromStoryboard:#"FabIdeas"];
[self.navigationController pushViewController:horizontalListController animated:YES];
Now for pushing another view controller named WishlistViewController which is present in storyboard named Wishlist:-
UIViewController *WishlistViewController = [UIViewController instantiateViewControllerWithIdentifier:#"WishlistViewController" fromStoryboard:#"Wishlist"];
[self.navigationController pushViewController:WishlistViewController animated:YES];

Storyboard segues and receiving memory warning

I am developing an application with iOS 9 based SDK , this is my first time I am working with Storyboards , I have 20 view controllers, each scene has Next / Previous buttons to go back and forward . I have a huge problem with going forward !. If I move from scene 1 to for example to scene 15 I received memory warning and then application crashes . I have searched and it seems there is method called unwind segue but it seems this is for going back ! it's something like dissMiss method .
I connect each scene with line in Interface Builder :
Here is segue's setting :
I would be grateful if you help me out .
EDITED :
I tried to present a view controller programmatically but result was the same ! .
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
WhatIsDino *vc = (WhatIsDino*)[mainStoryboard instantiateViewControllerWithIdentifier:#"WID"];
[self presentViewController:vc animated:YES completion:nil];
Seems like it's a problem of wrong approach, and not the storyboard.
Let me guess, since before storyboard you used to change your app's rootViewController to the next/previous screen once you tap on the arrow button. So previous screen are released and deallocated from memory once you set a new rootViewController.
And now you're presenting every next view controller modally, which involved creating new UIWindow and loads all the hierarchy of you screen and keeps previous underneath the new one so it holds the memory and you're getting out of memory crash.
Well, you can do rootViewController approach with a storyboard too since it's just another way to manage your screens while development. Storyboard offers additional features like segues, static table view cells, general tint color and so on. [UIStoryboard -instantiateViewControllerWithIdentifier:] is the method you might find interesting.
But I'd rather recommend you to check out the UIPageViewController, it's like a container for the screens. Unfortunately, it cannot have the segues to your scenes (because of the special way segues work) so you have to use -instantiateViewControllerWithIdentifier: method anyway. You can treat inner view controllers of UIPageViewController as you do with rootViewController before.
You can also navigate without segue and Its easy way I think.
If you want to navigate from Class1 to Class 2 then follow these steps.
1) In Class 1, Import Class2.
2) In your button Action, Write this code.
Class2 *next = [self.storyboard instantiateViewControllerWithIdentifier:#"Class2 Identifier name"];
[self.navigationController pushViewController:next animated:YES];
Do not forget to give Identifier name in story board that is "Storyboard ID" in Attribute inspector of particular class.
No need to add Segue,Your storyboard would look clean.
The problem is that you are adding view controller after view controller with modal presentation. That causes each view controller to be added on top of the previous one, and all of them accumulate, using more and more memory.
Using a navigation controller and a push also piles the view controllers on top of each other.
You will have this problem if you use storyboards, nibs, or create the view controllers manually.
If you have a design where the user can move through a large series of view controllers then you probably want to dismiss the previous one before pushing/presenting a new one.
You can probably dismiss the previous view controller without animation and then present the new view controller each time you want to display a new one and avoid the memory issue. i'd have to experiment with it to get the effect I was after, but that's what I would suggest.

Storyboard not unloading when switching to another xib

i'm developing an app for the iPhone, and i'm having a bit of trouble with switching views and xibs and storyboards etc. I have gotten everything to work as in switching the views, but when i change to my game view from my main menu, it seems that instead of entirely switching views, it just appends all the objects i added programmatically etc to the same storyboard for some reason, i can see the previous page in the background while the game completely loads and works. The buttons from the previous views also seems to still still work, but are just "ghost buttons" as in i can click them through the new view.
It's almost as if the old view never unloads but just stays there in the background, while the new view gets added on top of the old view. I'm using Xcode Version 5.0.1 (5A2053) and developing for iOS 7. the methods i use for switching views are:
From Storyboard to Xib:
[self presentViewController: [iphoneGame alloc] animated: NO completion:nil];
From Xib to Storyboard:
ViewController *viewController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"ViewController"];
[viewController setModalPresentationStyle:UIModalPresentationFullScreen];
[self presentViewController:viewController animated:NO completion:nil];
Your code to load a VC from an XIB is wrong. You don't create a view controller with alloc/init. You need to use initWithNibName:bundle:. If you pass in nil for both, the system should use the nib name that's the same as the class name.
Why are you trying to mix and match VCs from XIBs and storyboards? That's fairly unusual. Most people either use one or the other, and if you're going to do it, the burden is on you to get everything set up correctly.

Move between multiple storyboards by replacing the windows root controller

To move between VCs on a single storyboard, I use the first custom segue from the post :
bidirectional storyboard travel without stacking This replaces the window's root view controller with the destination view controller, so the VCs do not stack and cause memory allocation issues.
I need to use multiple storyboards and so I am after a method of moving to a second storyboard that replaces the windows root controller with the initial VC of the new storyboard (I.e in a similar way to the custom segue I have used throughout the rest of the project.)
The solution should ideally work for IOS6 & IOS7 (the pseudo-segue method has been updated to IOS7 only)
Any ideas?
You can't do this with a segue. Segues can only be made between controllers in the same storyboard. The only way to do this, is in code by instantiating that first controller, and setting it as the window's root view controller.
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"SomeOtherStoryboard" bundle:nil];
NewController *new = [sb instantiateInitialViewController];
self.view.window.rootViewController = new;

Resources