I have the following situation of my app. Four child VCs need to have a data source delegate, which their root VC (they all share one root VC) must implement.
My question is - do I declare a protocol in each child VC and implement each protocol individually in the root VC, or is there a better way? Somehow define the protocol only once?
Thanks
No need to declare 4 protocols in 4 child VC's.
Take only one protocol just like #elio.d said and implement that protocol method in your Root VC.
What about having each Child that Inherit from a controller that declare the protocol you need?
#protocol VCChildDelegate <NSObject>
-(void) yourMethod;
#end
#interface WCChildAbstract : UIViewController
#property (nonatomic,assign> id<VCChildDelegate> delegate
#end
Related
I have many viewcontrollers which needs to have some common functionality related to navigation.
Earlier I made a base class BaseViewController(extending UIViewController) which have all common functionality (like doing some tasks on viewDidLoad etc) and all my viewcontrollers extends BaseViewController.
The problem is that some of my viewcontroller should be subclass of UIViewController and some of UITableViewController, so I can not use above approach.
One way could be to write base class for both and duplicating code. Is there any better way without duplicating code.
While you can get around this by using delegation or helper objects, I would make the case for just not using UITableViewController. It is only a very light subclass on top of UIViewController, providing a table view, conforming to the delegate & data source protocols, and adding a property or two for selection & refresh.
While I wouldn't normally suggest recreating something that the framework has already done for you, it may (in your case) make your code more easy to understand if you just keep everything inheriting from a common base class and add a table view to one of the subclasses.
If you do think this would be a reasonable approach, the UITableViewController documentation overview gives a detailed description of exactly what & where these behaviours are implemented, so mimicking its exact setup is trivial.
Adding a table view to UIViewController
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) IBOutlet UITableView *tableView;
#end
In your storyboard, drag a "Table View" from the object library and drop it on top of your View Controller scene's "View" in the Document Outline - this will replace the root view with a UITableView.
Then just hook it up:
ctrl-drag from the view controller to the table view to hook up the view and tableView outlets
ctrl-drag from the table view to the view controller to set the delegate and dataSource outlets.
Done - no magic required.
I'm trying to use a base class MyBaseViewController that's a subclass of UIViewController for every view controller that I'll use in my app, so that I could have some things set up in that base controllers for convenience.
I created a storyboard with a UITableViewController object in it, and I'm writing a custom controller class MyTableViewController for it.
I wanted MyTableViewController to subclass MyBaseViewController and implement UITableViewDelegate and UITableViewDataSource for it to work properly with a table view, but when I tried to set the class for the UITableViewController object in my storyboard, it doesn't allow me to select the custom class I wrote.
I assume this is because the storyboard wants me to use a class that's a sub class of UITableViewController?
Is there a way to get this to work without changing MyTableViewController's inheritance?
Unfortunately not. Objective C doesn't support multiple inheritance.
I don't know what kind of defaults you're setting up in MyBaseViewController but if you moved them to an extension of UIViewController you could then have MyTableViewController and MyBaseViewController both call the same extension methods.
In a word, no. UITableViewController's ancestry is already defined. It inherits directly from UIViewController. You can't insert a new ancestor after the fact.
Objective-C doesn't have multiple inheritance, which would make this easy.
You could define your own subclass of UIViewController that implements the table view delegate and data source methods, and use that instead of a UITableViewController as the base class for your table view, but some things won't work unless the host of your table view is a UITableViewController.
If you don't need to add instance variables to your view controllers or override existing methods you might be able to define a category of UIController (UIViewController+MyMethods).
You might also create a set of methods that you add to both a custom subclass of UIViewController and UITableViewController. If you put your extra source code into a separate source file you could #include that source file in the body of your MyViewController.m. and your MyTableViewController.m files:
You might have a file ExtraVCMethods.m:
- (void) methodOne
{
//code
}
- (void) methodTwo
{
//code
}
And then in your MyViewController.m:
#implementation MyViewController
#include "ExtraVCMethods.m"
#end
And in your MyTableViewController.m:
#implementation MyTableViewController
#include "ExtraVCMethods.m"
#end
(Note that I am #including a .m file, not a .h file, and the #include goes inside the implementation, which is unusual.)
The #include would cause the compiler to insert the same source in the body of both classes. That way if you change the source for your extra methods you don't have to change it in 2 places.
Note that with the above approach you turn off the target membership checkbox for your ExtraVCMethods.m file. You don't want that compiled separately - just pasted into your other source files by the precompiler.
I recently asked a question regarding my incorrect use of instance variables on UIViewControllers. The answers I got brought to light another fundamental issue: I think I might be using UIViewControllers all wrong as well.
My typical storyboard looks something like this:
As you can see, each view controller has a custom class. One of those custom classes might look like this:
// SecondViewController.h
#import <UIKit/UIKit.h>
#import "MasterViewController.h"
#interface SecondViewController : MasterViewController
#property (strong, nonatomic) NSData *incomingData;
#end
// SecondViewController.m
#import "SecondViewController.h"
#import "ThirdViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do stuff with data, then pass it on to next view controller.
}
#end
Each view controller is a subclass of MasterViewController. In MasterViewController, I'll usually put global things like user state validation, for example. That way every view controller in the app can validate user state without reimplementing it each time.
About that incomingData property...I wasn't sure how else to pass data between UIViewControllers without using a property on a custom class...hence three separate custom classes, each one not doing all that much.
So here's where it gets murky for me...
Should I be keeping important methods and instance variables in a "master" view controller so all other view controllers can subclass it and have access to those methods and variables? Or is that ridiculous?
Am I causing more work for myself by not using proper class extensions or categories? Would those help me keep things a little more streamlined and organized?
My current system works for the most part, but it can get unwieldy when I have lots and lots of view controllers, each with its own custom class.
Obviously, the first step which you may have done, and whether you've completely grasped or not, is to really understand the Model-View-Controller pattern. A pretty good tutorial I did with a quick google search is here: http://codewithchris.com/how-to-make-iphone-apps-mvc-one-pattern-to-rule-them-all/.
The next thing I'd suggest is that your controllers don't need to be subclasses of anything but UIViewController. There's a difference between the view hierarchy and the class hierarchy. In your storyboard, you're outlining a view hierarchy - which view/controller leads to the next view/controller. They can all be independent. They can still pass data even without being subclasses of each other, or of a previous controller.
You can still pass data directly through properties. Why not be more specific about what data needs to be sent? Instead of sending a generic NSData blob, specify what you need one controller to tell the next. For example, if you're passing in a username from one to the next, use:
#property (nonatomic, strong) NSString *username;
for the second controller, and in your first controller, set it:
self.secondController.username = #"my name";
If from the second controller to the third controller, you don't need the username, don't make it a property of the third controller. Maybe the third controller only displays a number:
#property (nonatomic, strong) NSNumber *someNumber;
From the second controller, just set its value:
self.thirdController.someNumber = #5;
Now, I like to use some global variables and methods. Those are sometimes useful when you have helper classes that don't need to be instantiated. One way to do this is to just have a class variable:
FirstController.h:
+(NSString *)defaultName;
FirstController.m
+(NSString *)defaultName {
return #"Default";
}
Then in second controller, you can do something like
self.username = [FirstController defaultName]
There are other patterns, like delegate patterns, where the second controller asks its delegate (the first controller) what the username should be:
self.username = [self.delegate username];
But for this you have to set up a protocol. Ray Wenderlich usually has very good tutorials: http://www.raywenderlich.com/46988/ios-design-patterns
What you have here is Strong coupling, which is considered to be a bad thing.
Lets say you want to create a new application, and you want to copy a view controller from the old application, at the moment it would be very difficult for you as you would have to modify a lot of things.
The goal is to achieve weak coupling, which means that every class in your project can work independently from one another.
You can store your data as a property on your App Delegate, which is a singleton, so you can get an reference from an any class using AppDelegate *appDelegate=[UIApplication sharedApplication].delegate;. Or you can define a new singleton object to store the data.
The pattern you are using is dependency injection, which many people prefer to the singleton pattern.
If all of your view controllers are inheriting from the same superclass (MasterViewController) and all have that same property then you can simply define the property in the superclass.
So, I'm teaching myself iOS development and Objective-C. I completely new to Xcode and Objective-C but not programming.
I might be going about this completely wrong, but I'm trying call a method from a parent view controller from the child view controller. I tried to use delegation, but I ran into the problem of adopting multiple protocols of different types to the same class. Since I have a table view in my parent view controller, I'm unsure on how to adopt my child view controller delegation.
Essentially, I want to adopt both of these at the same time:
#interface MainViewController : NSObject <AddSiteViewControllerDelegate>
and
#interface MainViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
I'm new to the language, so this question might have a simple answer.
Since you want both at the same time, it has to be as below
#interface MainViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, AddSiteViewControllerDelegate >
UIViewController is a child of NSObject, so by inheriting UIViewController, you would get the behaviors defined in NSObject.
Objective-C supports single inheritance only. That is, a given class can derive from only one other class. However, a class can implement any number of protocols. So a simple solution to your problem would be:
#interface MainViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, AddSiteViewControllerDelegate>
I have two controllers in the storyboard, embedded in a NavigationController, and there is a segue to switch between these.
Passing data from the first controller to the second one is pretty straightforward by implementing prepareForSegue, and set the properties of the second controller using segue.destinationViewController.
I should pass back data to the from the second controller to the previous one also. I googled, but I have not found any simple, but working code to demonstrate it.
Would you be so kind give me a simple sample about the best way to do it?
Thanks in advance!
In your second view controller class you create a protocol and delegate. The first view controller will set it self as the delegate in prepareForSegue and implement the protocol methods. The second view controller will then call the methods to pass data back to the first view controller. Here is some code from one of my projects as an example.
#protocol TableSelectorDelegate <NSObject>
#optional
- (void)didMakeSelection:(id)selectionString forType:(NSString *)dataTitle;
- (void)didAddNewValue:(NSString *)newValue forType:(NSString *)dataTitle;
#end
#interface TableSelectorViewController : UITableViewController
#property (nonatomic, weak) id<TableSelectorDelegate> delegate;
#end
when you set the data you're passing to the second controller you can also set a pointer to the previous one.
The "recommended" way of doing this is using a delegate. Have the first view controller set itself as the delegate of the new view controller during the -prepareForSegue: call, then when you're done, you call whatever delegate methods you've defined.
This is a bit more work than tightly coupling the two controllers, but it actually saves time if you ever find you need to use the controller in a slightly different way. If you watch the WWDC'11 video on using IB and Storyboards, they actually go through this pattern in depth and include code examples and demos, so I recommend taking a look at that.
I've been studying all of the variants to this question of how to pass data from one view controller to another and have come to see that Apple's Second iOS App Tutorial has not only the code but a lovely explanation of everything involved.