I'm trying to find the best way to refactor this. Right now, each of the view controllers (about 20 of them) have a function that initializes the content of the toolbar / navigation bar like so ie,
-(void)toolbarInit
and on each of the controller's viewDidLoad, you will see that the function is being called ie, [self toolbarInit];
Basically what that toolbarInit does is to put a loginButton on the navigation bar's rightBarButtonItem.
Should I:
a. put the toolbar/nav bar setup in the app delegate ( is it possible to initialize each of the view controller's nav bar no matter how deep down the stack it is already?)
b. create a toolbarController or something and just put all the setup code/login code over there?
Many thanks for any other suggestions.
I'd consider one of these options:
It sounds as though you're repeatedly pushing the same or similar UIViewControllers onto the stack. Is it possible they should all derive from the same UIViewController subclass? If so, the initialization could take place in a superclass shared by all of the 20 or so items in the stack.
If the view controllers have different superclasses, consider defining a category on UIViewController that handles the instantiation and addition of your buttons. Then you need only import the header and call the same [self toolbarInit] in viewDidLoad.
Same as above, except instead of using a category, create a standalone class for handling setup like this. The logic in toolbarInit could be stuck in a class method like setupNavigationItem:.
Related
I have implemented Facebook type left Slide Bar layout in my first view of iphone app. Now, I want to implement this throughout all view controllers (screens) in application, means irrespective of the view the left slide bar should appear on clicking the menu button at the top in all views.
My app contains 25-30 viewcontrollers and my slide bar layout should appear in all views..
Can anyone suggest, how can I include above FB Layout in all views
Thanks in advance
Ramu
Simple, The one view controller in which you have implemented the FB layout and is working. Make it the base class on top of UIViewController. And as for the rest of all the ViewControllers, inherit them from the MasterClass that you just created. Doing this will make the swipe gesture that brings forth the slide bar available to all of your 30 view controllers.
EDIT
Lets see, we have UIViewController, now first of all you create a UIViewController's subclass: say FBViewController ..In this FBViewController you implement the FBLayout such that the swipe and all is working ..on it ..test your app first using only this FBViewController as rootViewController and check all the functionalities.Once everything is working fine, grow on it. What I mean is this.
Say you are creating a Tabbed application, where all the three tabs are supposed to have the same FBLayout style. Then do these steps.
Create FBViewController, it inherits from UIViewController (using UIViewController subclass template, also check the generate XIB button) also have an XIB for it FBViewController.XIB (fully implement FBLayout in it. This will be your base class)
Then Create three more ViewController classes (FirstViewController, SecondViewController, ThirdViewController) again from the UIViewController subclass template, but for these three dont check the generate XIB button. these three will use the XIB of the base class FBViewController (If you are wondering how, then go to step 3 :))
Go to header file of FirstViewController class you created, there you can see #interface FirstViewController: UIViewController replace it with #interface FirstViewController: FBViewController, but before it import FBViewController.h to the header file. Repeat the same for the Other two classes- SecondViewController, ThirdViewController. Since these three will inherit from FBViewController. In their viewDidLoad [super viewDidLoad] will load FBViewController and generate the view. after [super viewDidLoad]; line you can implement ur own methods.
In the three classes just change the initWithNibName method to change the tab bar name and title.
In appDelegate go to didFinishLaunching method and put these three view controller in a tabBarController, set the tabBarController as rootViewController.
And we are done. If your FBViewController is working fine. You will see that all the three classes behave the same way. Thanx to the power of Inheritance.
Cheers, play a bit, have fun.
I had the same problem. I was using a facebook-style menu, and needed it in all view controllers.
You can use a Container Controller. A Container Controller can have the base layout, which I defined in a nib, containing a navigation bar and a bar button item to toggle the menu, and then add child view controllers and remove them as you need them. That way, you can throw whatever view controller you need to the container controller and it will display it.
You can also add gesture control to slide open/close the menu easily.
You will have to make the Container controller your self, it is not standard. I think it is better solution than inheritance, since if you use inheritance you can't make a for example UITableViewController, all your controllers will be of the type of yuor master class. Of course, you can fix this anyway with delegates.
It may sound a bit tricky, but see this tutorial which I used: http://www.cocoanetics.com/2012/04/containing-viewcontrollers/
It wasn't accutally that hard.
EDIT: You can just use a UINavigationController as well. Just set the base view controller to the view controller you want to display, and you can prevent it adding the back button etc to the nav bar by overriding the default methods. Make a UINavigationController as rootNavigationController. Might be simpler.
I'd highly recommend using an open source solution that handles all the edge cases for you - it's both the easiest, most robust and most maintainable (since the community will keep it up to date fro you). ViewDeck seems to be the most popular solution though I have also had success with PPRevealSideViewController. They both provide a very robust implementation that would take a long time to do yourself (e.g. you can optionally enable swipe on the navigation bar or even content area to open the menu). Furthermore they separate the sliding logic and the revealed menu (which can be any view controller you like, but most likely a table view controller) out of your other view controllers. That way any viewcontroller can have a side menu without duplicating any code - separation of concern is great :)
You can make a SharedInstance for SideView class. I am doing same thing for iAD to show throught-out the application.
Please see the the link of iAdSuite ,In which the BannerViewController is SharedInstance so they are easily used for all View Controller
http://developer.apple.com/library/ios/#samplecode/iAdSuite/Listings/TabbedBanner_TabbedBanner_BannerViewController_m.html
I have a tab bar as a root view controller each tab is a navigation controller with detail view controller as a root.
I need several tabs that have almost same logic but separate values for properties. I thought the best way is to use the same vc and set properties in the init method. Something like:
[[MyViewController alloc] initWithStyle:MyCustomStyle]
But I don't see where to call this method.
I assume you're creating your UI using a nib or a storyboard. If so, yes, doing custom initialisation of UIViewControllers or subclasses isn't supported -- the designated initialiser is called for you.
Possible workarounds:
create your UITabBarController programmatically: this way you can instantiate your VCs any way you like
have MyViewController find out which instance it actually is upon instantiation and set itself up accordingly -- by, for example, looking at its containing view controller and poking around with that (e.g. finding out what tab index it is)
The first option is the far nicer one, since it's cleaner and doesn't involve MyViewController having to know about its potential placement in a UITabBarController or similar (which is very hacky).
If you are relying on NIBs to instantiate and initialize your nav controllers, you can use several tricks to tell each one what it is.
Simpliest one is to tse Tag in NIB, and handle different values in initWithNibName:bundle:, initWithCoder:, or viewDidLoad
I'm not going to say this is good design by any means. I'm kind of inheriting something that is existing. Anyway, there is a TabController. One this one tab, there are two views that get loaded on demand based on a UISegmentController. Both of these two ViewControllers are subclasses of another ViewController that has methods I need.
When I'm in the TabController, I want to create a method that uses some of the superclass methods of the two ViewControllers. How do I get access to the tab's current ViewController since it's loaded on demand? Do I need to have a reference of the base controller type, and just have it set to the current view controller when it gets loaded on demand? Thanks.
To get the active tab showing on the screen, you could use [self.tabBarController selectedViewController] which will give you a UIViewController Reference. If you want to use the methods then you can cast it to your ViewController Superclass and then call methods on it like so (where self.tabBarController is your Tab Bar Controller):
MySuperClassViewController *viewC = (MySuperClassViewController *)[self.tabBarController selectedViewController];
[viewC someMethodDeclaredHere];
First, a little bit of terminology to clear up some potential confusion: I assume you mean a UITabBarController which controls various view controllers. Also, it seems you are using a UISegmentedControl which is not a view controller but a subclass of UIView. I hope these are just typos and not conceptional problems.
There is still some ambiguity about what you mean with "when I am in the tab controller". I assume you want to put code into the class representing the UITabBarController. But why? Just put these methods into the appropriate view controller, or if it is something that has to be done before, into your app delegate. However, if these methods are in a view controller superclass, they should have something to do with the logic necessary for this view controller.
If you need the methods elsewhere, i.e. outside your view controllers, consider creating a separate #include file and putting the methods there. Alternatively, you can use your app delegate which is conveniently accessible through [[UIApplication sharedApplication] delegate].
I have my main program with MainAppDelegate.h,MainAppDelegate.m.
I have created two custom navigation controllers ANavController, BNavController classes and have created added the controllers in Interface Builder and assigned my custom classes to the two controllers inside the MainWindow.xib
When the application first loads I make it render the first NavControllers and its view in the didFinishLaunchingWithOptions method:
[window addSubView:ANavController.view];
This is works fine and loads the first navigation controllers view. My problem is that within the view on each nav controller, inside the viewDidLoad method I have created a UIBarButtonItem which I add to the right side of the navcontroller.
I'm trying to make this button call an action method defined inside my MainAppDelegate.m.
For both of my two custom NavController classes I have set the delegate to MainAppDelegate inside interface builder. I try to define buttons like so:
[flipButton addTarget:self.delegate action:#selector(changeModeAction:)
forControlEvents:UIControlEventTouchUpInside];
and obviously then inside MainAppDelegate.m I have defined a method:
-(void)changeModeAction:(id)sender
This method should flip the ANavigationController currently in the window to he BNavigationController.
But it obviously crashes on the addTarget:self.delegate. What's the proper way to do this?
Basically I'm trying to add a button to the top right of each NavControl which will fire a FLIP page animation, switching to the other NavController and it's stack.
So if you're two levels deep on ANavController and you hit the top right button, it will flip to BNavController and wherever you were last in it's stack. Hitting the button again will flip the page again back to ANavController and you'll still be two levels deep, before you switched to B. Does this make sense?
I think my idea implementation needs some work?
Sorry for posting this as an answer instead of a comment - my reputation is too low to add comments - but could you please provide the error message?
EDIT: See thread below for details - the problem was that 'self' was not a subclass of UINavigationController and therefore did not have a delegate property. Changing this to self.navigationController.delegate worked.
I have a UIViewController — let's call it "FormController" — which is simply a form that edits an object. I want to use it in 2 different situations:
Creating a new object — using using UINavigationController's presentModalViewController: method.
Editing an existing object — push the view controller onto the UINavigationController stack, not using a dialog method.
There is a slight difference in that in the modal situation I would like to have a toolbar with "Cancel" and "Done" buttons, whereas in the stack situation I would like to just have the navigation bar provided by the UINavigationController.
This would be similar to the Contacts application where the "New Contact" and the "Edit Contact" screens seem to use the same view controller, but the New Contact form is presented modally while the Edit screen is pushed onto the navigation stack.
My question is: What is the best way to handle both situations without having to write 2 separate, but mostly identical view controllers?
I thought about creating a "ModalFormController" which encapsulates the bare "FormController" through composition and adds a toolbar, but I read somewhere in the docs that Apple doesn't recommend nesting view controllers.
Why not use subclassing? Make ModalCreateFormController a subclass of EditFormController and handle the modal-specific stuff in the subclass.
What I do (sometimes) is set up an enum that specifies the type of the view controller.
For example, you might have two types: an Edit type, and an Add ("new") type.
The Add type is implemented via a modal view controller, while the Edit type is pushed onto an existing navigation stack.
In the view controller's -viewDidLoad: method, I simply do a switch/case tree that sets up the title and other appearance characteristics depending on the type enumeration specified above.
The nice thing about this is that it is easy to add a new type. The downside is that the conditional tree for handing this enumeration can get complicated quickly, depending on how different the types are.
But the switch/case tree makes it a lot easier to manage.
So, it depends on what you're trying to do with the two types. But it's definitely doable.
In addition to having an explicit property on the view controller (as Alex Reynolds suggests), two other approaches that occur to me are:
If you have some kind of model object that you're editing, ask it for its current state. If it's ever been saved, then you're in edit mode. Otherwise, you're in create mode.
Look at the value of the controller's parentViewController property. If it's an instance of UINavigationController, then you're in the navigation stack. If you're being displayed modally, it'll be an instance of your list controller.
Ug, I hate extra ivars…
I use this instead:
if([[self.navigationController viewControllers] objectAtIndex:0] == self){
//Modal
}else{
//Pushed
}
It is a bit of a hack, but we are using the logic that if the offending view controller is the first in the stack, you can't go back. Actually we are ignoring the fact of whether it is modally displayed at all.
I had to do this a bunch of times in my app and after trying a couple different ways of doing it, including modal subclasses & a re-usable modal helper classes that used forwardInvocation. I found the best pattern was to make a containingModalViewController method each view controllers that (usually) creates and returns a UINavigationController for the caller to use with presentModalViewController.
In most cases this method builds and returns a UINavigationController with self as the root view controller (with repeated calls to the method checking self.navigationController and returning that instead if it's not nil). Other cases I made a dummy root controller first and pushed self on second in order to get a back button. Then a trick can be used to catch the back button press: http://smallduck.wordpress.com/2010/10/05/intercepting-uinavigationcontroller/
In some cases the view doesn't need a navigation bar and so this method just adjusts some flags and returns self. I even found in some cases that did need a navigation bar it was simpler to make that method invoke self.view, then tweak the view hierarchy to add a UINavigationBar and again return self. But in any case, the setup often isolated to that one method, and the caller handles it the same in each case.
Apple explains how the contacts application works under the hood:
To allow a custom view controller class to be used to both display and edit content, override the setEditing:animated: method.
You get some functionality for free, e.g. Edit/Done button.