I' m trying to extract the UITableView Delegate method into a base class called BaseTableProvider
//BaseTableProvider.h
#protocol RSTableProviderProtocol <NSObject>
- (void)cellDidPress:(NSIndexPath *) atIndexPath;
#optional
- (void)cellNeedsDelete:(NSIndexPath *) atIndexPath;
#end
#interface RSBaseTableProvider : NSObject <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) NSMutableArray *dataSource;
#property (nonatomic, weak) id<RSTableProviderProtocol> delegate;
#end
//BaseTableProvider.m
#implementation RSBaseTableProvider
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc]init];
return cell;
}
#end
And then I create a subclass of BaseTableProvider, and override two UITableViewDelegate Method in the subclass
//RSFeedListTableProvider.h
#interface RSFeedListTableProvider : RSBaseTableProvider
#end
//RSFeedListTableProvider.m
#implementation RSFeedListTableProvider
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [self.dataSource count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
RSFeedCell *cell = (RSFeedCell *)[tableView dequeueReusableCellWithIdentifier:kFeedCell];
if(cell == nil){
cell = [[RSFeedCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kFeedCell];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
Feed *feed = (Feed *)self.dataSource[indexPath.row];
if (feed != nil) {
cell.titleText = feed.title;
cell.subTitleText = feed.summary;
}
return cell;
}
#end
I initialized the ListTablerProvider in a ViewController.
//RSFeedListViewController.m
#implementation RSFeedListViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
_feedListView = [[RSFeedListView alloc] init];
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
_provider = [[RSFeedListTableProvider alloc] init];
_provider.delegate = self;
return self;
}
#pragma mark -- Life Cycle
- (void)loadView{
RSFeedListView *aView = [[RSFeedListView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.feedListView = aView;
self.view = aView;
self.feedListView.tableView.delegate = _provider;
self.feedListView.tableView.dataSource = _provider;
}
But I can't see the cell built on the screen.I debuged the code found that UITableView Delegate Method in RSFeedListTableProvider was not called.Any one can help me?Thanks!
I think you did not implement the numberOfRowsInSection datasource method in your subclass RSFeedListTableProvider. So it will invoke the super class implementation
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 0;
}
and there you are returning zero, so cellForRowAtIndexPath in your subclass never will be called.
Solution is implement numberOfRowsInSection in subclass and return proper count
Maybe you are inherited NSObject instead UITableViewController! And you need to create property UITableView *tableView, and connect to your UITableViewController dataSource and delegate.
Related
I have a UITableView in my app and I'm trying to pull it's delegate methods into a separate UITableViewDelegate. This is what the code looks like:
RestaurantViewDelegate *delegate = [[RestaurantViewDelegate alloc] initWithRestaurant:self.restaurant andRecommended:self.recommended];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 235.0f, self.view.frame.size.width, self.view.frame.size.height-235)];
self.tableView.delegate = delegate;
self.tableView.dataSource = delegate;
[self.view addSubview:self.tableView];
And this is what RestaurantViewDelegate looks like:
// RestaurantViewDelegate.h
#interface RestaurantViewDelegate : NSObject <UITableViewDelegate>
#property (nonatomic, strong) NSArray *recommendations;
#property (nonatomic, strong) Restaurant *restaurant;
- (id)initWith Restaurant:(Restaurant *)restaurant andRecommended:(NSArray *)recommendations;
#end
and
// RestaurantViewDelegate.m
#implementation RestaurantViewDelegate
#synthesize recommendations = _recommendations;
#synthesize restaurant = _restaurant;
- (id)initWith Restaurant:(Restaurant *)restaurant andRecommended:(NSArray *)recommendations {
self = [super init];
if ( self != nil ) {
_recommendations = recommendations;
_restaurant = restaurant;
}
return self;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Recommendations: %d", [_recommendations count]);
return [_recommendations count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 48.0f;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
#end
However, when I run my app and click on a cell, all cells disappear. I really don't know what's causing this. Any ideas as to what I'm doing wrong?
This is a very interesting problem. Remember that in ARC (Automatic reference counting ), an object will be retained only as long as a strong reference to it is maintained. Remember, the 'delegate's are always weak, and in your case it means, once you come out of the scope, where you create the delegate object and setup the table view, there will no longer any delegate object retained. This is the reason you might not see anything happening when you try to reload the table view. Make the delegate object RestaurantViewDelegate a member of your controller. And check..
I'm having an issue with displaying cells in my UITableViewController, simply..nothing shows up when I go to test. Below is my code.
FormationViewController.m
#import <Foundation/Foundation.h>
#import "FormationViewController.h"
#interface FormationViewController ()
#end
#implementation FormationViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.names count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Title"
forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"Title"];
}
NSString *name = _names[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#", name];
return cell;
}
- (void) tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = #"Formation View";
self.tabBarItem.image = [UIImage imageNamed:#"tab_icon_formation"];
self.names = #[#"John Doe", #"Lee Harvey Oswald", #"Pat Buchanan", #"Angry Orchard",
#"Sierra Nevad", #"Jeff Bridges", #"John McClain", #"Lucy Genero"];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"The length of names is %lu", self.names.count);
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.formationVC.frame = CGRectMake(
0,
self.topLayoutGuide.length,
CGRectGetWidth(self.view.frame),
CGRectGetHeight(self.view.frame)
- self.topLayoutGuide.length
- self.bottomLayoutGuide.length
);
}
- (void)loadView {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor whiteColor];
self.formationVC = [[UIView alloc] init];
self.formationVC.backgroundColor = [UIColor whiteColor];
[view addSubview:self.formationVC];
self.view = view;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
#end
And my FormationViewController.h
#import <UIKit/UIKit.h>
#interface FormationViewController : UITableViewController
#property (strong, nonatomic) UIView *formationVC;
#property (strong, nonatomic) NSArray *names;
#end
I'm also not sure if I'm putting some of these lines of code in the right methods.
I also think that my problem might be lying in the tableView:cellForRowAtIndexPath: method. I'm not absolutely sure I'm implementing that 100% correctly.
If anyone can help me find out what I'm doing wrong please let me know. This is my first Obj-C/iOS project I'm working on too so please be gentle! Thank you!
Well, I tried your code , using no xibs and faced the same problem.
Then I changed 2 things
removed LoadView.
moved self.names intialization from initWithNibName to viewDidLoad
added
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Title"];
[self.tableView reloadData];
to viewDidload
And it worked
I think you' re never stop in (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ... if you instantiate your view controller from a storyboard, you need to implement initWithCoder, or just put your names initialization in viewDidLoad.
As you are probably using Storyboard, check these steps:
1) Storyboard - select your table view controller and in Identity Inspector make sure you have it's class set to your FormationVC.
2) Storyboard - select table view inside the controller and select it's first cell (suggesting you use Prototype Cells). Make sure it's style is set to Basic and set it's identifier for example to "BasicCell".
3) Go to your code and use this:
FormationVC.h
#import <UIKit/UIKit.h>
#interface FormationVC : UITableViewController
#end
FormationVC.m
#import "FormationVC.h"
#interface FormationVC ()
#property (strong, nonatomic) NSArray *names;
#end
#implementation FormationVC
#pragma mark - View Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// you must not forget to call your custom init method once the view is loaded
[self setupView];
}
- (void)setupView {
// set title of VC
self.title = #"Formation View";
// create array of names
_names = #[#"John Doe", #"Lee Harvey Oswald", #"Pat Buchanan", #"Angry Orchard",
#"Sierra Nevad", #"Jeff Bridges", #"John McClain", #"Lucy Genero"];
// log number of names in names array
NSLog(#"The length of names is %lu", _names.count);
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_names count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"BasicCell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%#", _names[indexPath.row]];
return cell;
}
#end
Problem IS there :-
#property (strong, nonatomic) NSArray *names;
change NSArrayTo NSMutableArray
in.h
EX:- #property (nonatomic , retain) NSMutableArray *names
and in.m
names = [[NSMutableArray alloc] init];
and then alloc init your Array and then put value its work .
I have a view controller with a static cell named 'Make' I have two controllers one called "AddCarTableViewController" and "MakeTableViewController" when you click on the static cell named 'Make' it presents the make table view controller where you can select the make, then pops the view controller and am trying to store the selected value in the detailTextLabel of the static cell. here is my code for all the controllers.
The problem I'm having is once I select the make everything happens as it should I even log the selected item and it saves it after popping the view controller, but I can't figure out how to implement selected item into the detailTextLabel. Any help will be much appreciated!
"MakeTableViewController.h"
#import <UIKit/UIKit.h>
#import "AddCarTableViewController.h"
#protocol CarMakeDelegate <NSObject>
- (void)updateCarMake:(NSString *)updateMake;
#end
#interface MakeTableViewController : UITableViewController
#property (nonatomic, strong) NSArray *carMakes;
#property (nonatomic, weak) id <CarMakeDelegate> delegate;
#end
MakeTableViewController.m
#import "MakeTableViewController.h"
#interface MakeTableViewController ()
#end
#implementation MakeTableViewController {
NSIndexPath *oldIndexPath;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.carMakes = [[NSArray alloc] initWithObjects:#"Acura", #"Aston Martin", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.carMakes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [self.carMakes objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
oldIndexPath = indexPath;
NSString *addMake = self.carMakes[indexPath.row];
[self.delegate updateCarMake:addMake];
NSLog(#"%#", addMake );
[[self navigationController] popViewControllerAnimated:YES];
}
#end
AddCarTableViewController.h
#import <UIKit/UIKit.h>
#import "MakeTableViewController.h"
#interface AddCarTableViewController : UITableViewController
#property (strong, nonatomic) NSString *makeName;
#property (weak, nonatomic) IBOutlet UITableViewCell *makeCell;
#end
AddCarTableViewController.m
#import "AddCarTableViewController.h"
#interface AddCarTableViewController ()
#end
#implementation AddCarTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 4;
}
-(void)updateCarMake:(NSString *)updateMake {
self.makeCell.detailTextLabel.text = updateMake;
}
#end
You don't need to use delegate in this case. Just update the underlying data model. and call
[tableview reloadData];
When the makeViewController is popped.
In the AddCarVC's cellForRowAtIndex, add another line to check if current indexPath corresponds to Make cell and if it does update the detailLabel text.
I have a TableView displaying a list of domain :
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.tableView.dataSource = mainClass.domainList;
}
The domain list is set up like that :
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.domainList count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.textLabel.text = [[self.domainList objectAtIndex:indexPath.row] name];
return cell;
}
This works perfectly, it displays each domain in a row of my table view.
Now i would like to add a "Sub TableView" in each cell of my Table View to display a list of documents related to the domain.
I tried that :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableView *table = [[UITableView alloc] init];
table.dataSource = [self.domainList objectAtIndex:indexPath.row];
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.textLabel.text = [[self.domainList objectAtIndex:indexPath.row] name];
[cell.contentView addSubView table]
return cell;
}
It doesn't crash but it doesn't work neither. I mean the sublist doesn't appear anywhere.
What am i doing wrong?
datasource must be a class implementing the protocol UITableViewDataSource. It looks like you are setting it to a custom object. Create a separate class with the code you used to implement the first table, then set the sublist as source data. In the objc.io article “clean table view code” they explain how to make reusable datasources. Or you can just give it a try on your own.
Consider this code:
// ARRAYDATASOURCE.H
#import <Foundation/Foundation.h>
typedef void (^TableViewCellConfigureBlock)(id cell, id item);
#interface ArrayDataSource : NSObject <UITableViewDataSource>
-(id) init __attribute__((unavailable("disabled, try initWithItems:cellIdentifier:configureCellBlock")));
- (id) initWithItems:(NSArray *)anItems
cellIdentifier:(NSString *)aCellIdentifier
configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock;
- (id)itemAtIndexPath:(NSIndexPath *)indexPath;
#end
// ARRAYDATASOURCE.M
#import "ArrayDataSource.h"
#interface ArrayDataSource ()
#property (nonatomic, strong) NSArray *items;
#property (nonatomic, copy) NSString *cellIdentifier;
#property (nonatomic, copy) TableViewCellConfigureBlock configureCellBlock;
#end
#implementation ArrayDataSource
- (id)initWithItems:(NSArray *)anItems
cellIdentifier:(NSString *)aCellIdentifier
configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock
{
self = [super init];
if (self) {
self.items = anItems;
self.cellIdentifier = aCellIdentifier;
self.configureCellBlock = [aConfigureCellBlock copy];
}
return self;
}
- (id)itemAtIndexPath:(NSIndexPath *)indexPath
{
return self.items[(NSUInteger) indexPath.row];
}
#pragma mark UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier
forIndexPath:indexPath];
id item = [self itemAtIndexPath:indexPath];
self.configureCellBlock(cell, item);
return cell;
}
#end
I have a custom view for my tableview's header
UIView *headerTableview_;
#property (nonatomic, retain) IBOutlet UIView *headerTableview;
I have connected it to xib file.
Then in .m file,
#synthesize headerTableview = headerTableview_;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return headerTableview_.frame.size.height;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
return headerTableview_;
}
Then, I try to run it, in iOS 6 it's displayed well, but in iOS 4.3, it's not displayed at all.
What could be the problem? Anyone knows how to fix this weird problem?
Thanks!
In your .h file
#interface testViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
UITableView *tableView;
}
#property (retain, nonatomic) IBOutlet UIView *testView;
#end
In your .m file
#implementation testViewController
#synthesize testView;
- (void)viewDidLoad
{
[super viewDidLoad];
tableView = [[UITableView alloc]initWithFrame:self.view.frame style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource =self;
[self.view addSubview:tableView];
[self.view bringSubviewToFront:tableView];
// Do any additional setup after loading the view, typically from a nib.
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return testView.frame.size.height;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
return testView;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Products";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];}
//UIImage *currentTweet = [[xmlParser tweets] objectAtIndex:1];
cell.textLabel.text= #"text label";
cell.detailTextLabel.text=#"detailTextLabel";
return cell;}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
[testView release];
[super dealloc];
}
#end
Create a new class file named HeaderTableview. See if this initializes properly when assigning in IB.
//.h
#class HeaderTableview;
#interface ViewController : UIViewController
#property (nonatomic, retain) IBOutlet HeaderTableview *headerTableview;
//.m
#synthesize headerTableview = _headerTableview;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return _headerTableview.frame.size.height;
}