How can I subclass a UITableView? - ios

I want to subclass a UITableView as I want to create a reusable table view component in my application.
The idea is instead of using a delegate for say cellForRowAtIndexPath I want the table view itself to get that call.
I don't think I want a UITableViewController as this UITableView that I want to build has to live in various UIViewControllers (and these UIViewController might have UITableViews of their own).
I subclassed my UITableView as:
#interface ShareUITableView : UITableView
but none of its methods get called.
My ShareUITableView is created via the NIB by setting the custom class to ShareUITableView. I have verified in code that a ShareUITableView is instantiated.
My UITableView does not delegate to its view controller, so that's not the problem.
Any ideas?

If I understood you, you need this class declaration:
#interface ShareUITableView : UITableView <UITableViewDataSource>
And then, in your class constructor, you should assign the instance itself as its own datasource:
- (id)init
{
//...
self.dataSource = self;
//...
}
Of course, the class will have to adopt the protocol.
Good luck!

MyTableView.h
// MyTableView.h
// This overrides the UITableViewDataSource with your own so you can add any methods you would like.
#protocol MyTableViewDataSource <UITableViewDataSource>
#required
// This is where you put methods that are required for your custom table to work (optional)
- (int)myRequiredMethod;
#optional
// This is where you put methods that are optional, like settings (optional)
#end
// This overrides the UITableViewDelegate with your own so you can add any methods you would like.
#protocol MyTableViewDelegate <UITableViewDelegate>
#required
// This is where you put methods that are required for your custom table to work (optional)
#optional
// This is where you put methods that are optional, like settings (optional)
#end
// Make sure you add UITableViewDelegate and UITableViewDataSource implementations.
#interface MyTableView : UITableView <UITableViewDelegate, UITableViewDataSource> {
// Your customer datasource and delegate.
id <MyTableViewDataSource> myDataSource;
id <MyTableViewDelegate> myDelegate;
}
#end
MyTableView.m
// MyTableView.m
#import "MyTableView.h"
#implementation MyTableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// This is how you can use your custom method.
int i = [myDataSource myRequiredMethod];
}
return self;
}
- (void)awakeFromNib {
// This assigns the delegate and datasource you assigned to File's Owner in your xib to your custom methods
myDataSource = (id<MyTableViewDataSource>)self.dataSource;
myDelegate = (id<MyTableViewDelegate>)self.delegate;
self.delegate = self;
self.dataSource = self;
}
// This is an example of how to override an existing UITableView method.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// This calls the method implemented in your ViewController. See Below.
NSInteger rows = [myDataSource tableView:tableView numberOfRowsInSection:section];
return rows;
}
#end
MyViewController.h
// MyViewController.h
#import "MyTableView.h"
// Use MyTableViewDataSource and MyTableViewDelegate instead of UITableViewDataSource and UITableViewDelegate
#interface MyViewController : UIViewController <MyTableViewDataSource, MyTableViewDelegate> {
#end
MyViewController.m
// MyViewController.m
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
// This method will be overridden by myTableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (int)myRequiredMethod {
return 2;
}
Subclassing is a great way to make reusable custom UI elements.

I think, you should still go with a Controller class. I expect subclassing UITableView to be tedious work — if possible with reasonable amount at all.
There is no problem to have UIViewController/NoViewController implemented the delegate and datasource and yet assign another controller to a specific tableView. note, that the datasource and delegate don't need to be subclasses of UITableViewController.
have a look at this answer: Implement Delegate at Run Time?
My UITableView does not delegate to its view controller, so that's not the problem.
You have to have to use delegate and datasource, that is how TableViews are filled and configured. otherwise you will have to overwrite every method of UITableView — including private ones, a no-go if you want into AppStore. Recreating UITableView without subclassing it would be even easier.

Related

Use custom init method when programmatically creating a uicollectionview

Due to the limitations of Storyboard I am creating a UICollectionView programmatically. This is working all fine and when I want to add a UICollectionViewCell I do the following:
[collectionView registerClass:[Cell class] forCellWithReuseIdentifier:#"ID"];
What I was wondering is how can I use a custom init method from the class "Cell", because I can't do something like the following:
[collectionView registerClass:[[Cell class]init_custom]forCellWithReuseIdentifier:#"ID"];
Question: How can I use a custom init method from a custom UICollectionViewCell class?
If I understand you correctly, then I would create subclasses of your collection view cell.
First setup your cell with everything you want.
#interface MyCollectionViewCell : UICollectionViewCell
// Your custom cell
#end
#implementation MyCollectionViewCell
// Your custom cell
#end
Then for each collection view create a subclass which only overrides init.
#interface MyCollectionViewCellForCollectionView1 : MyCollectionViewCell
#end
#implementation MyCollectionViewCellForCollectionView1
- (instancetype)init // Only override -init
{
self = [super init];
if (self) {
// Setup for collection view one
}
return self;
}
#end
#interface MyCollectionViewCellForCollectionView2 : MyCollectionViewCell
#end
#implementation MyCollectionViewCellForCollectionView2
- (instancetype)init // Only override -init
{
self = [super init];
if (self) {
// Setup for collection view two
}
return self;
}
#end
Then for each different collection view, you register one of your subclasses.
[collectionView1 registerClass:[MyCollectionViewCellForCollectionView1 class] forCellWithReuseIdentifier:#"ID"];
[collectionView2 registerClass:[MyCollectionViewCellForCollectionView2 class] forCellWithReuseIdentifier:#"ID"];
This will get you the separate custom init methods you wish, but be sure to keep all your functionality in the base class.

REUSE a UITableView with its UI/delegate methods

I have 2 UIViewControllers, the 2 ones are containing EXACTLY the SAME UITableView(with its custom cells and delegate methods).
My question is their any way to "centralize" the UITableView UI and code(datasource and delegates), so that I just have to modify in one file instead of 2 .
following up on my comment, the table view in the xib in your father vc and the delegate methods in your father vc are just in the same place because you chose it to be like that, the table view and the delegate methods are actually quite detached.
so create a new object, say FatherTableController which implements UITableViewDatasource and UITabelViewDelegate and copy those methods out of your FatherViewController into this FatherTableController
now in your FatherViewController, go like
FatherTableController tableController = [FatherTableController new]; //should be a property or a singleton
self.tableview.delegate = tableController;
self.tableview.datasource = tableController;
now you can do that in both your separate vc's that use the same table, and even use the exact same table contoller between the two views if you share it in some way (possibly via a singleton pattern, which can be useful for sharing state between the two view controllers)
Solution:
#interface FatherViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *parentTableView;
#implementation FatherViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.parentTableView.delegate=self;
self.parentTableView.dataSource=self;
}
//declare the delegate / datasource methods
--------------------- CHILD VIEW CONTROLLER ---------------------
#interface ViewController : FatherViewController
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate=self;
self.tableView.dataSource=self;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [super numberOfSectionsInTableView:tableView];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [super tableView:tableView numberOfRowsInSection:section];
}

How to create a Common Custom TableView Class?

I want to create a custom tableView class, Which can be used in any view Controller. I have to just create tableview object and set an array and frame of tableview . Then this tableview will be add as subview on my view. and also give me a click event.
I just want to avoid writing tableview datasource and delegate method in every viewController class.
Take a viewController or tableviewController class and code all the delegates and data source methods there. now in you view controller where you want to make it as a subview call the tableview class and add it as a subview.
EX:
TableviewContrller *libaray =[TableviewContrller new];
[libaray willMoveToParentViewController:self];
[self.view addSubview:libaray.view];
[self addChildViewController:libaray];
To hide write this code in your tableview controller class
[self.view removeFromSuperView];
As you are using a reusable class you need to send the array information to that class. along with it it will be better to send either class name or setting tag value to tableview
So in your tableview class write this
-(id)initWithInformationArray :(NSMutableArray *)dataArray andTagValueforTableview :(int) tagValue
{
self = [super init];
if (self != nil)
{
NSLog(#"%#", dataArray);
}
return self;
}
Now sub viewing will be like this
TableviewContrller *libaray =[[TableviewContrller alloc]initWithInformationArray:YOURARRAY andTagValueforTableview:TAGVALUE];
[libaray willMoveToParentViewController:self];
[self.view addSubview:libaray.view];
[self addChildViewController:libaray];
Hope this will help.
May be you can use UITableViewController.
UITableViewController is a subclass of UIViewController, when you create a subclass of UITableViewController, the template has the usual methods of tableview datasource and delegate methods.
You'll need to create a custom class and create your own delegate in that class for UITableView. Now whenever you create a UITableView assign that custom class as the class for UITableView.
If you don't know how to create custom delegates then check below links:
http://www.alexefish.com/post/522641eb31fa2a0015000002
http://ios-blog.co.uk/tutorials/quick-tip-create-your-own-objective-c-delegate-protocol/
Hope this will help you :)
You can create BaseTableView class.
#interface BaseTableView : UITableView <UITableViewDelegate, UITableViewDataSource>
{
NSArray* listObject;
}
#property (nonatomic, retain) NSArray *listObject;
-(id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
#end
#implementation BaseTable
#synthesize listObject;
-(id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
if(self = [super initWithFrame:frame style:style])
{
self.dataSource = self;
self.delegate = self;
}
return self;
}
-(void)setListObject:(NSArray *)listObjectRef
{
[listObject release];
listObject = [listObjectRef retain];
[self reloadData];
}
-(void) dealloc
{
[listObject release];
[super dealloc];
}
#end
Inherit this class for specific use and override following methods according to needs
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
In your ViewController class use following code
SpecificTableView *table = [[SpecificTableView alloc] init];
[table setListObject:((FRFTReportList*)obj)];
Hopefully this will help.

UIViewController, UITableView, Protocol

I have a UIViewController, which has a
#interface MYViewController : UIViewController<UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tblView;
Now I am implementing the UITableViewDelegate because I want to use the tableview.
so in my viewdidload I do
- (void)viewDidLoad
{
[super viewDidLoad];
self.tblView.delegate = self;
//self.tblView.dataSource = self;
}
Now the Datasource setting throws a warning, and I never hit the
cellForRowAtIndexPath
method.
Sorry about the editing.
What am I doing wrong?
you need
self.tblView.datasource = self;
also adopt to UITableViewDatasource Protocol,
#interface MYViewController : UIViewController<UITableViewDelegate,UITableViewDatasource>
cellForRowAtIndexPath is a datasource method.
Your interface should also implement the UITableViewDataSource protocol.
#interface MYViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
{
....
}
Please try as below..
UIViewController.h
#interface MYViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>
#property (Strong, nonatomic) IBOutlet UITableView *tblView;
please don't forget to connect tblView outlet to tableView in your .xib file, since this is very required otherwise your datasource methods won't get called.
In UIViewController.m file
- (void)viewDidLoad { [super viewDidLoad];
self.tblView.delegate = self;
self.tblView.dataSource = self; }
and implement two required datasource methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return (number of row you want in table)}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ return cell }
surely this will work for you.
For UITableViewDatasource Protocol add UITableViewDatasource to
#interface MYViewController : UIViewController<UITableViewDelegate, UITableViewDatasource>
and add this line to viewDidLoad
self.tblView.datasource = self;
and go to your storyboard and link 'datasource' and 'delegate' to your tblView
There are couple of problems in your implementation.
You are not conforming to UITableViewDatasource protocol.
You are not setting tableview datasource.
You are not implementing UITableViewDatasource protocol's required methods
You cannot expect to work without setting datasource.

custom protocol doesn't work

In the app I'm working on, I have a UIViewController sublcass and a UIView subclass. in the storyboard the view controller contains the UIview. in the uiview I'm drawing something but I need it to know some values that it should be getting from the view controller. So I created a custom protocol in the view controller .h file:
#protocol SSGraphViewControllerProtocol <NSObject>
- (void)numberOfSemesters:(int)number;
#end
#property (weak, nonatomic) id <SSGraphViewControllerProtocol> delegate;
and in the UIView class I confirmed it as having the protocol above and I implemented its method. However. when I pass a number from the view controller, UIView doesn't receive it. Using NSLog, I figured out that UIView isn't entering - (void)numberOfS:(int)number; am I doing anything wrong? How can I fix it? and is there another way that I can send data from the UIViewController class to the UIView controller?
Here is the full code:
UIViewController.h
#protocol SSGraphViewControllerProtocol <NSObject>
- (void)numberOfSemesters:(int)number;
#end
#interface SSGraphViewController : UIViewController
#property (weak, nonatomic) id <SSGraphViewControllerProtocol> delegate;
#end
UIViewController.m
#implementation SSGraphViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.delegate numberOfSemesters:2];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
UIView.h
#interface SSGraph : UIView <SSGraphViewControllerProtocol>
#end
UIView.m
static int numberOfS = 0;
#implementation SSGraph
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
SSGraphViewController *graph = [[SSGraphViewController alloc] init];
graph.delegate = self;
return self;
}
- (void) numberOfSemesters:(int)number{NSLog(#"YES");
numberOfSemesters= number;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
}
Read This Article, It is best example with Description
http://css.dzone.com/articles/do-not-publishcreating-your
Also read for create Protocol
Following i describe simple Example for How to create protocol
#DetailViewController.h
#import <UIKit/UIKit.h>
#protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
#end
#interface DetailViewController : MasterViewController
#property (nonatomic, assign) id<MasterDelegate> customDelegate;
#DetailViewController.m
if([self.customDelegate respondsToSelector:#selector(getButtonTitile:)])
{
[self.customDelegate getButtonTitile:button.currentTitle];
}
#MasterViewController.m
create obj of DetailViewController
DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];
and add delegate method in MasterViewController.m for get button title.
#pragma mark -
#pragma mark - Custom Delegate Method
-(void) getButtonTitile:(NSString *)btnTitle;
{
NSLog(#"%#", btnTitle);
}
You're creating a view controller instance inside of initWithFrame:, assigning its delegate to be self, and then not keeping a reference to the controller or adding its view into the view hierarchy. This is certainly not what you meant to do. Make the connection in your storyboard instead, by making the delegate property an IBOutlet and connecting them by right clicking on the view controller and dragging from the circle next to the property name onto your view instance.
As an aside I'm not convinced of the utility of using a protocol in this way. If the view needs to know some information to do its job, if should either expose some properties that can be set by the controller, or declare a dataSource protocol and query its dataSource rather than rely on the view controller defining the interface it needs.
// Add an observer to your ViewController for some action in uiview
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveActionNotification:)
name:#"someActionNotification"
object:nil];
// Post Notification and method in your Viewcontroller will be called
[[NSNotificationCenter defaultCenter] postNotificationName:#"someActionNotification" object:self];
// at the end Dont forget to remove Observer.
[[NSNotificationCenter defaultCenter] removeObserver:#"someActionNotification"];

Resources