Are modal view controllers the preferred way to replace the entire interface on an iPad? - objective-c

Specifically, I have something like a game, with a menu screen made out of standard components. I want a button to switch to another view controller that the user will interact with for a while, then return to the menu screen. It seems like having the menu controller present the 'game' mode as a modal view controller is the most straightforward solution, but is this the best way to essentially replace the entire view? Is the whole menu (which may later become a deep nav or split controller) kept in memory as long as the modal controller is in front, and is this something I should bother to worry about?

There are really two parts to this question:
Which method of transitioning from one view to the next in an iPad application provides the best experience to the user?
Which method of transitioning from one view to the next is easiest to implement and best handles memory management?
I'm not going to try to address the first part of this question other than to point you to Apple's 'iPad Human Interface Guidelines' which says (among other things):
Reduce Full-Screen Transitions
Closely associate visual transitions with the content that’s changing. Instead of swapping in a whole new screen when some embedded information changes, try to update only the areas of the user interface that need it. As a general rule, prefer transitioning individual views and objects, not the screen. In most cases, flipping the entire screen is not recommended.
When you perform fewer full-screen transitions, your application has greater visual stability, which helps people keep track of where they are in their task. You can use UI elements such as split view and popover to lessen the need for full-screen transitions.
http://developer.apple.com/library/ios/prerelease/#documentation/General/Conceptual/iPadHIG/DesignGuidelines/DesignGuidelines.html
However, in your case I'd have thought a full-screen transition is entirely appropriate (but then I'm not a user experience expert).
In answer to the second part, yes displaying a new view controller modally seems like a good approach to take.
By default both the objects used by the menu view and those used by the modal view will be kept in memory - but the great thing about using UIViewController sub-classes is that they've got some default memory management built-in. If your application receives a memory warning whilst the modal view is being presented in full-screen mode, the menu view controller's views will be removed and it's 'viewDidUnload' method will be called. So in your implementation of this method you should release any objects you don't need and then recreate them as needed in the menu view controller's viewDidLoad method (which will be called again before the menu view is shown).
This is explained in more detail in the UIViewController class reference:
When a low-memory warning occurs, the UIViewController class purges its views if it knows it can reload or recreate them again later. If this happens, it also calls the viewDidUnload method to give your code a chance to relinquish ownership of any objects that are associated with your view hierarchy, including objects loaded with the nib file, objects created in your viewDidLoad method, and objects created lazily at runtime and added to the view hierarchy. Typically, if your view controller contains outlets (properties or raw variables that contain the IBOutlet keyword), you should use the viewDidUnload method to relinquish ownership of those outlets or any other view-related data that you no longer need.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html

Related

Completely unload a ViewController at modalTransition?

normally after a modal transition, the second viewController runs further "behind" the visible ViewController in the background.
Is there a possibility to completely unload the second ViewController ??
If don't want to use push, because I want an animation on the transition...
Several points:
You are worrying about something that is not a worry. View controllers are lightweight, simple objects. The only heavyweight object is the view, and it is in fact removed from the interface when another view controller's view is presented in its place.
The game does not "run in the background". If it does, you're doing it wrong. You should be detecting in viewDidDisappear: that your view is no longer in the interface and stopping all the activity. That's what these sorts of events are for.
If you're still bent on making certain the view controller itself is destroyed when the game is not showing, then use a different architecture. For example, present the game controller. When the game controller is dismissed, it will be released and destroyed.

Two view controllers (with nibs) acting on the screen at the same time

Just as a disclaimer, I am an iOS beginner, in a pretty unique position. I'm an intern learning on the job, with a hard deadline for a test app, with specific specs. So I unfortunately don't have the luxury of learning all that I should about xCode and objective C.
Anyways, I am trying to create a navigation menu similar to the iPad's slide out menu. I've looked at plenty of sample code given in response to questions like this in the past. It all works perfectly fine, but I can't understand all of what they're doing. I think this results from being fairly bewildered by view controllers and delegates. And, since I'm supposed to be writing this all by myself, I can't just build off of their existing code.
My plan for this working is to have one main view controller, containing the navigation menu in a table view. This is hidden behind a normal UIView, until a button is pressed, at which point the normal UIView slides offscreen enough to reveal the menu. Upon selection of a menu item, the normal UIView would slide back to its original position, and be replaced by the relevant UIView, controlled by its view controller. In other words, clicking on the menu item relating to "Home" would load the homeViewController.xib, controlled by the homeViewController. This would be loaded in that normal UIView subview, on top of the tableView.
I'm able to load a view in that normal UIView as a result of a button press.
homeViewController *_homeViewController = [[homeViewController alloc]initWithNibName:#"homeViewController"];
[self frontView] = _homeViewController.view;
There may be some syntax errors in that code, since its from memory. I'm not able to access the computer with my code on it at the moment, but that's the general gist. Basically, that places the home view nib on the frontView (that normal UIView that's over the table view), but its not connected to homeViewController. Or at least I think that's the issue, when I press a button, that's correctly connected between the nib and the homeViewController.h, and only set to NSLog in the .m file, the application crashes.
Any idea how to connect this nib to its viewController when displayed in this way? Or, to create the nib in IB, without the associated .h and .m files, and use the main view controller as the homeViewController as well? Or, if my logic is inherently flawed, what SHOULD I do? Something with NavigationControllers?
EDIT:
I also tried a new approach- changing homeViewController's file owner to viewController, and connecting the button on homeViewController's action to viewController. This too caused a crash upon pressing the button. I really think the issue is with having multiple view controllers acting on screen at once, or having multiple views from separate nibs, controlled by one view controller on screen at once.
I suspect that your immediate problem is that _homeViewController is being freed as soon as you leave whatever method that code is in. To fix this, create a strong property in this class that holds the _homeViewController for as long as its view is needed and allocate it to that property rather than a local variable. That way, buttons (or whatever) that are part of that view still have a valid controller object backing them.
In the long run, pushing or presenting view controllers when you need the screen to change is a much better strategy but, as I said, the immediate problem.... :)

Access NSWindow's Element via Child NSView/ViewController

Is it possible to access a NSWindowController's element from a child NSViewController?
Essentially I have a NSProgressIndicator that spins on the bottom corner of the NSWindow. This works because my WebView is in my NSWindowController instead of my NSViewController.
I want to break the logic apart now but I'm having trouble understanding how I'd access these elements from my View Controller.
Thanks!
You can't connect an outlet from one NIB to an object in another and the desire to do so indicates a problem with your design. If the view is so intimately connected to other things in the window, maybe it shouldn't be separated out into a different NIB.
A view should only go into a separate NIB when it makes sense as a self-contained unit. It should represent and manipulate its controller's representedObject and not much more. The controller might have a delegate that it informs about what's being done and asks to make customizing decisions.
Maybe you can continue to use a separate NIB if you adopt that sort of design. Perhaps the window will have a reference to some model object. It would configure the view controller to use that model object as its represented object. And perhaps the progress indicator would be bound to that same model object. Then, as the view manipulates its represented object, it would indirectly also affect the progress indicator.
Another option would be for the window controller to set itself as the delegate for the view controller and your view controller could invoke it at appropriate points to inform it of things going on in the view. Then the window controller could do whatever was appropriate to the progress indicator or other stuff in the window outside of the view. This hypothetical delegate is something you would have to add to the view controller class and you'd design its protocol.

What's the best way to programmatically add and remove views and their controllers?

I'd love to see a detailed explanation on how to manage views programmatically. I'll provide an overview of how I'm doing it now and would like either comments on how my approach sucks or just an overview of how to do it cleanly and properly.
Basically, in my app's main view controller's loadView method, I first create a root view and set self.view to it. When I want to attach a controller, say, the first one that displays, I call this method:
-(void) attachViewForController:(UIViewController*)controller
{
[self.mRootView addSubview:controller.view];
[controller viewWillAppear:NO];
}
Notice that I explicitly call viewWillAppear (I believe it wasn't automatically calling it), where I manually perform any animations to bring the view in (sliding in, fading in, etc). Is there anything wrong or strange with this approach here?
Now, when I want to leave this view and switch to another, I call a method to setup the switch:
-(void) setControllerSwitch:(UIViewController*)outgoingController
incomingController:(UIViewController*)incomingController
delay:(float)delay;
{
self.mOutgoingController = outgoingController;
self.mIncomingController = incomingController;
self.mSwitchControllerTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(switchControllerCallback) userInfo:nil repeats:NO];
}
At this moment in time, I've begun the exit animations for the outgoing view, and this method records the controllers and schedules a method that will perform the actual switch at the moment the outgoing view is done animating. Like this:
-(void) switchControllerCallback
{
self.mSwitchControllerTimer = nil;
// remove outgoing view
[mOutgoingController.view removeFromSuperview];
// add incoming view
[self attachViewForController:mIncomingController];
}
Is this a decent way to manage things? A few points:
I know I could probably instead setup a callback to trigger when the outgoing controllers animations end, but chose to just do it via an explicit delay param to leave room to allow me to cross fade views. However, I think calling setControllerSwitch early may in fact not allow crossfading because it would junk the old controller early and make it chop off its animation.
As mentioned earlier, I'm curious to know if explicitly calling viewWillAppear is a no no and there is a more appropriate way to manage view flow.
Copied from Apple developer documentation but it help me lot to understand about views and maneging multiple views.
Tips for Using Views Effectively
Custom views are useful for situations where you need to draw something the standard system views do not provide, but it is your responsibility to ensure that the performance of your views is good enough. UIKit does everything it can to optimize view-related behaviors and help you achieve good performance in your custom views. However, you can help UIKit in this aspect by considering the following tips.
Views Do Not Always Have a Corresponding View Controller
There is rarely a one-to-one relationship between individual views and view controllers in your application. The job of a view controller is to manage a view hierarchy, which often consists of more than one view used to implement some self-contained feature. For iPhone applications, each view hierarchy typically fills the entire screen, although for iPad applications a view hierarchy may fill only part of the screen.
As you design your application’s user interface, it is important to consider the role that view controllers will play. View controllers provide a lot of important behaviors, such as coordinating the presentation of views on the screen, coordinating the removal of those views from the screen, releasing memory in response to low-memory warnings, and rotating views in response to interface orientation changes. Circumventing these behaviors could cause your application to behave incorrectly or in unexpected ways.
For more information view controllers and their role in applications, see View Controller Programming Guide for iOS.
Minimize Custom Drawing
Although custom drawing is necessary at times, it is also something you should avoid whenever possible. The only time you should truly do any custom drawing is when the existing system view classes do not provide the appearance or capabilities that you need. Any time your content can be assembled with a combination of existing views, your best bet is to combine those view objects into a custom view hierarchy.
Take Advantage of Content Modes
Content modes minimize the amount of time spent redrawing your views. By default, views use the UIViewContentModeScaleToFill content mode, which scales the view’s existing contents to fit the view’s frame rectangle. You can change this mode as needed to adjust your content differently, but you should avoid using the UIViewContentModeRedraw content mode if you can. Regardless of which content mode is in effect, you can always force your view to redraw its contents by calling setNeedsDisplay or setNeedsDisplayInRect:.
Declare Views as Opaque Whenever Possible
UIKit uses the opaque property of each view to determine whether the view can optimize compositing operations. Setting the value of this property to YES for a custom view tells UIKit that it does not need to render any content behind your view. Less rendering can lead to increased performance for your drawing code and is generally encouraged. Of course, if you set the opaque property to YES, your view must fills its bounds rectangle completely with fully opaque content.
Adjust Your View’s Drawing Behavior When Scrolling
Scrolling can incur numerous view updates in a short amount of time. If your view’s drawing code is not tuned appropriately, scrolling performance for your view could be sluggish. Rather than trying to ensure that your view’s content is pristine at all times, consider changing your view’s behavior when a scrolling operation begins. For example, you can reduce the quality of your rendered content temporarily or change the content mode while a scroll is in progress. When scrolling stops, you can then return your view to its previous state and update the contents as needed.
Do Not Customize Controls by Embedding Subviews
Although it is technically possible to add subviews to the standard system controls—objects that inherit from UIControl—you should never customize them in this way. Controls that support customizations do so through explicit and well-documented interfaces in the control class itself. For example, the UIButton class contains methods for setting the title and background images for the button. Using the defined customization points means that your code will always work correctly. Circumventing these methods, by embedding a custom image view or label inside the button, might cause your application to behave incorrectly now or at some point in the future if the button’s implementation changes.

Putting an ADBannerView on top of a UINavigationController

I'm trying to put an iAd banner in an app that is based on a UINavigationController (it's not the standard nav-base app proposed by xcode, cause I don't need the table view).
I'd like to place an ADBanner on its bottom, to be always visible, no matter how the user pops and pushes views.
I studied the iAdSuite example in the apple sample code, but, though it's reported among the "best practices", I don't think it's the best practice for what I need. It basically declares an ADBannerView in the app delegate class and then implements the ADBannerViewDelegate methods for every single view the app needs. That means implementing the ADBannerViewDelegate methods over and over again on every view controller class you need! It doesn't seem too smart... :(
I'd rather prefer to have an approach more similar to what Apple itself does in the tab bar based app, where you have a part of the window always occupied by the tab controller and all the views switching above without affecting the tab bar view below.
You can't directly put an ADBannerView along with the nav controller in the app delegate, because ADBanner needs to be placed in a view controller (you get a runtime error otherwise).
I tried to subclass from UIViewController, implementing the ADBannerViewDelegate in this class, and place it in the rootViewController along with a UINavigationController but I'm not having good luck with this approach...
Has anybody found a good, simple way to to this? Any hint?
Thank you for any help...
You can have just one class for ADBannerViewDelegate, and just one instance of ADBanner itself. When the currently active view changes, remove ADBanner from the old view, add it as a subview to the new view.
EDIT:
to clarify, you don't need each view implement the ADBannerViewDelegate. You only should have one class implement it (that class doesn't have to be a view controller for that matter).
You would also need to maintain a somewhere a property that would point to the currently active view (e.g. you can update that property in your Navigation Controller's navigationController:didShowViewController:animated:, or come up with your own protocol for that if your views appear in a more complex way).
Then in your ADBannerViewDelegate you'd just resize the view currently pointed to by that current view property. The actual view doesn't even have to know it has an ad in it ;)