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.
Related
Is it okay to define and implement methods in .h and .m file for Xib views or is it against MVC model concept? I am assuming the .h and .m files are part of the views and the methods that are defined and implemented in .h and .m files should only be initializing methods and the other action methods for button action should be implemented in the view controller that uses the .xib view. I have also seen methods for button action defined in the view's .m and .h file but I assumed the action should only be defined in the view controller.
Can you please explain if I am assuming wrong or my assumption of MVC is incorrect?
It depends on what are the methods intended to be for. The view can be enriched with methods if they are part of the view business. Like transformers that select colors from numbers, or code to manage complex controls. If the methods are not related to the model, why not?
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.
In my project I have two view controllers, and I am having trouble connecting objects such as an UIImageView to the view controller. When I try to create the IBOutlet, it tells me that "Could not insert new outlet collection: could not find any information for the class named UIViewController". I believe this problem stems from the fact that my original declaration of my class is as follows:
class UIViewController: UIViewController {
when in fact the view controller is named mainScene instead. However, when I change the first UIViewController to what I think it should be (mainScene), it doesn't even show me the option of connecting an IBOutlet...
class mainScene: UIViewController {
So, I have two questions.
Do I need to have a whole separate class for the second UIViewController and would that solve my issues?
Is there a better way to link objects to the UIViewController or am I just doing something horribly wrong (the likely scenario)?
Thanks so much
Short answer: 1. Yes, and yes. 2. There's no better way, and you're not doing something horribly wrong. (You probably just missed a step.)
You have two view controllers. Assuming they are different, you would subclass each one from UIViewController with a different name. E.g., mainSceneViewController and otherSceneViewController.
Your mainSceneViewController and otherSceneViewController would each have their own properties and IBOutlets.
Where you're probably stuck, is needing to change the class of your viewController within Interface Builder to match the class name in its .swift file, so IB knows what outlets it can connect for that view controller.
Each scene in your storyboard corresponds to a view controller. When the segue is performed, iOS instantiates your view controller from the storyboard.
While it is possible to only have one view controller subclass, and use the same subclass for different views, it doesn't happen too often.
Update:
Subclassing lets you add properties and methods to a class, and override their superclass.
In your comment, UIViewController is the class, and mainSceneViewController is subclassed from UIViewController. For your second view controller, it would likely be class otherSceneViewController: UIViewController {, as your other scene would likely require a different properties and methods from your main scene view controller.
You can read more about Inheritance in the Swift Programming Language guide.
I'm rather new to programming and wondering about a certain (best) practice:
Let's assume we have an app with several view controllers. In our case, most of those need the functionality of alerting the user about certain circumstances, make use of an activity indicator or depend on other similar general functionality. So far I've learnt how to implement such methods but then just copied the whole bunch of code to each view controller when needed. Doing so, every view controller gets filled up with a lot of extra code. I know it's possible to make code kind of "global" by moving it to the top of a view controller, outside the class brackets. But as we need to ensure that certain subviews are added to the right view controllers when calling those methods I'm not sure yet what the best way to go - in general - would be.
Is there a commonly used practice that differs from my approach when defining such - let's say - alert behaviors (defining variable/constant and its needed methods) which are used in multiple view controllers?
Objective-C provides two general ways of reusing code:
Inheriting a base class, and
Using a shared function.
The first case is straightforward: if you need a specific functionality in several view controllers, make a base view controller with the shared methods, and then derive your other view controllers from it:
#interface BaseViewController : UIViewController
-(void)sharedMethodOne;
-(void)sharedMethodTwo;
#end
#interface FirstViewController : BaseViewController
...
#end
#interface SecondViewController : BaseViewController
...
#end
#interface ThirdViewController : BaseViewController
...
#end
The second case can be implemented either as a helper class with class methods (i.e. with + instead of -) or with free-standing C functions.
You should look to see what could be moved from the controller level to the view level. It sounds like you're reusing a bunch of views so I would create subclasses of UIViews that can implement themselves. Then in the view controller you can handle when the views should appear and the data associated with them but you don't need to rewrite how to implement the views.
I want to create base class of UIViewController with tableview for my App sharing functionality between two almost similar ViewControllers.
When i Extend UIViewController with tableview did the child class get access to all views of its superViewController and all the (IBAction)undoAction:(id) sender methods?
Yes that should work. If you subclass a UIViewController (or any other NSObject) you will be able to access all of its (public) methods. You can create a public method by defining them in the header file and adding an implementation of that method in the implementation file. These files will both be created while creating a cocoa (touch) class. The same thing applies to properties, the one you define in the header file will be accessible.
Let me know if you have any further questions