I try to search this problem in this site and I found this link How to insert items to a UITableView when a UIButton is clicked in iOS. But my problem is, I already copy the code on that link and It doesn't reload the data when I insert a value to my array.
here's the code "ViewController.m"
#import "ViewController.h"
#interface ViewController ()
#property(nonatomic,strong) NSMutableArray * array;
#property(nonatomic,weak) IBOutlet UITableView * tableView;
#end
#implementation ViewController
-(NSMutableArray *) array{
if(_array==nil){
_array=[[NSMutableArray alloc] init];
}
return _array;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)addInfo:(UIBarButtonItem *)sender {
[self.array addObject:#"sample"];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TodoListItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
return cell;
}
#end
here's the code "ViewController.h"
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
#end
Is there something wrong with my code or there is something that I need to setup to my tableview? I'm very confuse of this and try to figure out the missing part of my code. I'm still studying the code of object c and I'm still noob for this. Please help me and thanks in advance.
How did you insert the UITableView?, on nib or programatically?, either way you need to flag the table's delegate and datasource, if on nib, right click on table and see that delegate and data source are connected to the files owner, if programatically check
self.MyTable.datasource = self
self.MyTable.delegate = self
please see how to check if connected [dataSource and delegate should be with a dot, if not click on circle and drag line to file's owner for both]
table delegate and datasource on interface builder "NIB"
Also please note that on this image outlet is not connected, you have to connected also to call the reload as you are doing now
edit, check if the button is connected?, put a log or a break point on ibaction for your button to know if is called
edit 2, you have to init the array, are you calling it? do this in view will appear or when you want to use it
edit 3, try this
- (IBAction)addInfo:(UIBarButtonItem *)sender {
if(self.array==nil){
self.array=[NSMutableArray array];
}
[self.array addObject:#"sample"];
[self.tableView reloadData];
}
Related
The app launches without any technical problems, although I can
not get the hardcoded categories to appear in table view cells from the .m file. The table view style is basic, and through that I thought I could reach the title sample through the .m file using:
self.items = #[# {#"name" : #" Chores", #"Category" : #"Home"}].mutableCopy;
At first I thought the problem occurred because I forgot to set a reuse identifier. But fixing that problem didn't help. I get no warnings or errors in Xcode. I have not modified any other files except the ViewController.m and main.Storyboard files.
Code from the .m file
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic) NSMutableArray *items;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.items = #[# {#"name" : #" Chores", #"Category" :
#"Home"}].mutableCopy;
//self.navigationItem.title = #"What2Do list";
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.items.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TodoItemRow";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *item = self.items[indexPath.row];
cell.textLabel.text = item[#"name"];
return cell;
}
#end
I expected "Chores" to show up in the table cell when I launch the app, but it will not appear.
In the ViewController.h you need to set the Delegate of the table view.
#interface ViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>
Also you need to link the tableview to the viewcontroller as its delegate. The easiest way is adding it in the storyboard.
First drag + ctrl your table to the viewController Symbol on the storyboard.
On the list click on delegate.
Repeat again for dataSource.
I have a TabBarController with 4 tabs, 3 of which are table views. I am trying to put a detail for every table view cell, and I don't think storyboard is efficient since I have over 50 detail pages. I'm very new to all of this, and I've tried to find out how to link a detail to every tab for a couple hours. My table views start with the Second View Controller.
Here is SecondViewController.m:
#import "SecondViewController.h"
#implementation SecondViewController
{
NSArray *tableData;
}
#synthesize tableData;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
tableData = [NSArray arrayWithObjects:#"Carter", #"Greene", #"Hancock", #"Hawkins", #"Johnson", #"Sullivan", #"Unicoi", #"Washington", nil];
[super viewDidLoad];
}
#pragma mark - TableView Data Source methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
#end
Here is SecondViewController.h:
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController <UITableViewDelegate,
UITableViewDataSource>
#property(nonatomic, retain) NSArray *tableData;
#end
If this helps, here is my storyboard.
If anyone can help me individually add details to each table view cell in the most painless way possible, I would appreciate it. Thanks!
If using storyboards, the process is fairly simple.
First, I'd suggest dragging a prototype "table view cell" on to your table views. You can then control-drag from that prototype cell to your destination scene to add a segue between the cell and the next scene:
Make sure to select that prototype cell and set its storyboard identifier (I used "Cell"). You will need to reference that storyboard identifier, as shown in my code sample below. I also configured appearance related things (like the disclosure indicator) right in that cell prototype in IB so I don't have to worry about doing that in code and I can see what the UI will look like right in IB.
Now you can go to the table view controller and (a) simplify cellForRowAtIndexPath (because you don't need that logic about if (cell == nil) ... when using cell prototypes); but also implement a prepareForSegue to pass the data to the destination scene:
// SecondViewController.m
#import "SecondViewController.h"
#import "DetailsViewController.h"
#interface SecondViewController ()
#property (nonatomic, strong) NSArray *tableData;
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableData = #[#"Carter", #"Greene", #"Hancock", #"Hawkins", #"Johnson", #"Sullivan", #"Unicoi", #"Washington"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController isKindOfClass:[DetailsViewController class]]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *name = self.tableData[indexPath.row];
[(DetailsViewController *)segue.destinationViewController setName:name];
}
}
- (IBAction)unwindToTableView:(UIStoryboardSegue *)segue {
// this is intentionally blank; but needed if we want to unwind back here
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.tableData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.tableData[indexPath.row];
return cell;
}
#end
Obviously, this assumes that you created a DetailsViewController and specified it as the destination scene's base class, and then create properties for any values you want to pass to this destination scene:
// DetailsViewController.h
#import <UIKit/UIKit.h>
#interface DetailsViewController : UIViewController
#property (nonatomic, copy) NSString *name;
#end
And this destination scene would then take the name value passed to it and fill in the UILabel:
// DetailsViewController.m
#import "DetailsViewController.h"
#interface DetailsViewController ()
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#end
#implementation DetailsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.nameLabel.text = self.name;
}
#end
Frankly, this process will undoubtedly be described more clearly in any UITableView tutorial includes a discussion of "cell prototypes" (your code sample suggests you were using an older tutorial that predates cell prototypes).
I think the relationship between the code and the storyboard is as following:
Code implement the function of the application.
Storyboard contains many scenes, these scenes implement the User interface, including data presentation, data input, data output.
Code read data from these scenes and output the result to the scenes.
Code is internal logic function entities and the storyboard the the User Interface presentation.
I am trying to implement the concept shown in this example project. My goal is to separate my view controller class and the datasource protocol. Instead of implementing the table view datasource methods in my table view controller class, I try to put it in its own class, and in my view controller, I only call this method to set up my table view:
- (void)setupTableView
{
void (^configureCell)(JVRTodoItemCell *, JVRTodoItem *) = ^(JVRTodoItemCell *cell, JVRTodoItem *todoItem)
{
[cell configureForTodoItem:todoItem];
};
NSArray *todoItems = currentUser.todoItems;
self.todoArrayDataSource = [[JVRArrayDataSource alloc] initWithItems:todoItems withCellIdentifier:TodoCellIdentifier withConfigureCellBlock:configureCell];
self.tableView.dataSource = self.todoArrayDataSource;
[self.tableView registerClass:[JVRTodoItemCell class] forCellReuseIdentifier:TodoCellIdentifier];
}
The data source is separated into its own class:
#interface JVRArrayDataSource ()
#property (copy,nonatomic) NSArray *items;
#property (copy,nonatomic) NSString *cellIdentifier;
#property (copy,nonatomic) void (^configureCellBlock)(id item, id cell);
#end
#implementation JVRArrayDataSource
...
#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;
}
The interesting part is that creating the cell based on the identifier (using dequeueReusableCellWithIdentifier:forIndexPath:) seems to be successful, because the correct cell gets allocated, but its labels remain nil. I try setting up my cell using the following method, but the values remain nil (aTodoItem has valid properties):
- (void)configureForTodoItem:(JVRTodoItem *)aTodoItem
{
self.todoItemTitle.text = aTodoItem.title;
self.todoItemPriority.text = [NSString stringWithFormat:#"%d", aTodoItem.priority];
}
I am trying to figure out what could possibly be missing here, but so far, I haven't managed to fix the issue yet, and I'm starting to lose hope. Any help would be appreciated.
UPDATE:
To make it clear, the issue is shown on this picture.
It seems that the cells get created, but its labels don't.
If all you want to do is separate your tableview datasource delegate from the view controller you can create a separate class called TableViewDataSource. Within that class you can manage the datasources and their table view cells; configuring the them in your view controller, but letting the TableViewDataSource manage them.
TDSTableViewDataSource.h
#import <Foundation/Foundation.h>
#protocol TDSTableViewDataSourceDelegate <NSObject>
- (NSString *)fetchCellIdentifierForObject:(id)object;
- (UITableViewCell *)configureCell:(UITableViewCell *)cell usingObject:(id)item;
#end
#interface TDSTableViewDataSource : NSObject <UITableViewDataSource>
#property (strong, nonatomic) NSArray *items;
#property (strong, nonatomic) id<TDSTableViewDataSourceDelegate> delegate;
#end
TableViewDataSource.m
#import "TDSTableViewDataSource.h"
#implementation TDSTableViewDataSource
- (NSArray *)items {
if (!_items) _items = [[NSArray alloc] init];
return _items;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if ([self.items count]) {
return [self.items count];
} else {
NSLog(#"numberOfSectionsInTableView could not be determined. self.items is nil or empty.");
return 0;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([self.items count]) {
return [self.items count];
} else {
NSLog(#"numberOfRowsInSection could not be determined. self.items contains fewer section requested does not contain any items.");
return 0;
}
}
/*
Single dimension Array of items belonging to a UITableView section
The method checks if the cell implements the HZConfigureTableViewCellDelegate, which is required.
The delegate should be the View Controller.
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id obj = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = nil;
if ([self.delegate conformsToProtocol:#protocol(TDSTableViewDataSourceDelegate)]) {
NSString *cellIdentifier = [self.delegate fetchCellIdentifierForObject:obj];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (obj)
cell = [self.delegate configureCell:cell usingObject:obj];
}
return cell;
}
#end
This class and the protocol will essentially allow you to fetch and configure UITableViewCell's and not have to implement the protocols into your View Controller.
Inside your view controller, you create a datasource property using the protocol above.
#import "TDSViewController.h"
#import "TDSTableViewDataSource.h"
#interface TDSViewController () <UITableViewDelegate, TDSTableViewDataSourceDelegate>
#property (strong, nonatomic) TDSTableViewDataSource *dataSource; // UITableView data source.
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation TDSViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.dataSource = self.dataSource;
self.dataSource.delegate = self;
}
#pragma mark - UITableView methods
-(NSString *)fetchCellIdentifierForObject:(id)object {
// Check if this is an event or a Reminder class.
if ([object isKindOfClass:[UITableViewCell class]]) {
// Return the cell identifier for this particular cell.
return #"com.myapp.defaultcell";
}
return #"blankcell";
}
- (UITableViewCell *)configureCell:(UITableViewCell *)cell usingObject:(id)item {
UITableViewCell *configuredCell = cell;
// Check if this is an event or a reminder.
if ([item isKindOfClass:[UITableViewCell class]]) {
// Configure the cell to present what data we want here...
}
return configuredCell;
}
#end
This is a complete example project. You can use this to configure any kind of cell you want, without having to add the datasource methods to your view controllers.
The view controller is used by the ConfigureTableViewCellDelegate protocol to configure the UITableViewCell's and use them in the Table View. Since the code is segregated now, the TableViewDataSource class now handles presenting the data to the table view. The View Controller is simply used to configure the cell's. This allows you to use custom UITableViewCells' on each ViewController if you want, and not have to deal with implementing the data sources each time.
UPDATED
Provided a better example, a complete project template.
In the ViewDidLoad register the nib, it fix the problem :)
-(void)viewDidLoad
{
[self.leftTableView registerNib:[UINib nibWithNibName:NIB_FILE bundle:nil] forCellReuseIdentifier:CELL_IDENTIFIER];
}
After hours of digging, I've managed to solve the issue (for now), by changing my custom cell's outlets to strong properties, and initializing them in the cell's init method:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.todoItemTitle = [[UILabel alloc] init];
self.todoItemPriority = [[UILabel alloc] init];
}
return self;
}
This is very strange, since I thought that creating my views in storyboard, this should be automatically taken care of, and I've never had to do this manually before.
I use Xcode 5 and storyboard where I use navigation controller and table view, and in the following code called RootViewController.h,
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController
#property NSMutableArray *objects;
#end
and RootViewController.m,
#import "RootViewController.h"
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[_objects addObjectsFromArray:#[#"Apple",#"Google",#"Intel"]];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"%d", _objects.count);
return _objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = #"A";
return cell;
}
#end
Xcode says "Build Succeeded" but the resultant screen doesn't have the rows rendered correctly.
It returns empty rows and title called Root View Controller.
Why?
I think I correctly write the three required method in numberOfRowsInSection, cellForRowAtIndexPath, and numberOfSectionInTableView, and I don't think I need to define protocol in header file since it's already defined in the parent UITableViewController.
And for some reasons the debug NSLog function in the numberOfRowsInSection method isn't executed.
So what am I missing here? And how can I fix it?
Thanks.
The problem is that _objects is nil - when you try to add objects to nothing the result is nothing.
So you need to create the array instead of adding to it. Change this line
[_objects addObjectsFromArray:#[#"Apple",#"Google",#"Intel"]];
to this
_objects = [NSMutableArray arrayWithObjects:#"Apple", #"Google", #"Intel", nil];
In your storyboard, you also need to set the cell identifier of the UITableViewCell (tutorials galore on how to do this).
You need to create objects before you can add anything to it:
- (void)viewDidLoad
{
[super viewDidLoad];
_objects = [NSMutableArray new];
[_objects addObjectsFromArray:#[#"Apple",#"Google",#"Intel"]];
}
Put inside viewDidLoad
self.title = #"some title";
To change the title of NavigationBar
Did you set the tableview delegate? Did you set the tableviewcell correctly in storyboard?
I'm a newbie programming iOS and I've a problem adding a new cell to a UITableView object. I'm using an storyboard and one of the scenes is a UIViewController that has several subviews: textfields, a tableview, etc. I intend to add rows to this tableView from a detail scene.
I'm able to initially add rows to the table, but I'm not able to add a row afterwards. When I press a button to add the row I call the method '[self.stepsListTableView reloadData];' which produces a call to the method '- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section' and it returns a correct value, including the new array element. But method '- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath' is not called to update the table.
I do not understand what I'm doing wrong.
Details of my source code:
WorkoutDetailsViewController.h
(…)
#interface WorkoutDetailsViewController : UIViewController <StepDetailsViewControllerDelegate, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id <WorkoutDetailsViewControllerDelegate> delegate;
#property (nonatomic, strong) Workout *workout;
(…)
#property (nonatomic, retain) IBOutlet UITableView *stepsListTableView;
(…)
WorkoutDetailsViewController.m
(…)
#synthesize stepsListTableView;
(…)
- (void)viewDidLoad
{
[super viewDidLoad];
addButton.enabled = FALSE;
workoutNameField.delegate = self;
if (self.workout == nil) {
self.workout = [[Workout alloc] init];
self.stepsListTableView = [[UITableView alloc] init];
}
self.stepsListTableView.delegate = self;
self.stepsListTableView.dataSource = self;
}
(…)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//return [self.workout.stepsList count];
NSInteger counter = 0;
counter = [self.workout.stepsList count];
return counter;
}
// Customize the appearance of table view cells.
- (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];
}
// Set up the cell...
// Pending
return cell;
}
- (void)stepDetailsViewControllerDidDone:(StepDetailsViewController *)controller
{
[self.workout.stepsList addObject:controller.step];
NSInteger counter = [self.workout.stepsList count];
[self.stepsListTableView beginUpdates];
NSArray *paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:(counter-1) inSection:0]];
[self.stepsListTableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop];
[self.stepsListTableView endUpdates];
[self.stepsListTableView reloadData];
}
(…)
Also in the storyboard, I have setup the outlets delegate and dataSource to be the controller view.
Any idea ?
Regards,
JoanBa
I have solved the issue. I have discovered using debugger that method reloadData was called for a different UITableView than the one initialized in viewDidLoad.
I reviewed the UITableView settings in storyboard, which aparently were correct but I have deleted them and created again. Now it works.
In UIViewController header I have the line
#property (nonatomic, retain) IBOutlet UITableView *stepsListTableView;
and in implementation I have commented lines
//self.stepsListTableView.delegate = self;
//self.stepsListTableView.dataSource = self;
And, of course, in storyboard I have defined for the UITableView the following relationships:
Outlets: dataSource, delegate --> UIViewController
Referencing outlet: UITableView --> UIVIewController
That's it !!
You may have the table view set to static content. Select the UITableView in Storyboard and in the "Attributes Inspector" section of the menu on the right of screen select the "Content" field under the "Table View" header and set the value to "Dynamic Prototypes".
Screenshot for clarity:
This little trick caught me out several times when I was starting out.