Already manage to pass input text from first VC to second VC with tableview.
First VC do not have tableview.
First VC:
UItextField - user type some name.
UIButton *add - button with segue (prepareForSegue)
Second VC:
TableView displaying input text from first VC with prepareForSegue
Question:
Tableview displays only one row at the time, so when i click back to input another name, and click add buton, tableview obviously gets reset and does not remember first input text. So how get tableview to remember names and put it in other rows. I don't know should i type code in prepareForSegue, or make delegate in first VC. Please explain in detail. Thank you alot.
I believe there are many ways to achieve this, if you want to persist data maybe core Data is your best bet, however if its just a simple logic then I suggest you using delegates.
ViewController
Interface
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *dataTextField;
#property (nonatomic) NSMutableArray *items;
- (IBAction)AddData:(id)sender;
#end
Implementation
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_items = [[NSMutableArray alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)AddData:(id)sender {
if ([self.dataTextField.text length]> 0) {
[_items addObject:self.dataTextField.text];
[self performSegueWithIdentifier:#"tableSegue" sender:self];
}else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Error"
message:#"You must enter some data"
delegate:self
cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
}
}
- (void)addItemViewController:(TableViewController *)controller didFinishSelectingItem:(NSMutableArray *)item selectedTag:(int)tag{
NSLog(#"DATA=%#", item);
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"tableSegue"]){
TableViewController *controller = (TableViewController *)segue.destinationViewController;
controller.items = _items;
}
}
#end
TableViewController
Interface:
#protocol TableViewControllerDelegate <NSObject>
- (void)addItemViewController:(id)controller didFinishSelectingItem:(NSMutableArray *)item selectedTag:(int)tag;
#end
#interface TableViewController : UITableViewController
#property (nonatomic, weak) id <TableViewControllerDelegate> delegate;
#property (nonatomic) NSMutableArray *items;
#end
Implementation
#implementation TableViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.navigationController popViewControllerAnimated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
[[cell textLabel] setText:_items[indexPath.row]];
return cell;
}
#end
View controllers are part of the controller layer of your application. They do not do "business logic" — heavy processing or more-persistent storage of data, whether to disk or just for that session.
The model section of your application handles that. Each view controller gets and sets data via the model. There should be no ongoing conversations between view controllers*; anything beyond things you would specify to an init is indicative of a broken design.
So you're asking the wrong question.
You would have a model that somehow vends the items that should go into the first view controller. You will have a second view controller that knows how to edit one item. The level of communication from first to second will be "this is the item you should be editing".
It is the responsibility of the first view controller and the model to ensure that it can keep its display up to date. The second view controller is responsible only for modifying its record. It shouldn't need to communicate anything whatsoever to the first view controller.
Whether you do that by pulling results from the model on every viewWillAppear, by some sort of live observation, by notifications emanating outward from the model or by some other means entirely doesn't matter.
(* subject to caveats where you've used containment, e.g. changes to the title that a view controller has but which is shown by a navigation controller are technically an ongoing conversation)
Related
I have ViewController and TableViewController. In ViewController i have a 2 buttons and 2 textfields, when button1 is clicked it will navigate to UITableView with static data like (Apple, Samsung, Blackberry, Windows),and button2 is clicked it will navigate to UITableView static data (Doctor, Engineer,Businessman, Employee)when i select any of the row it should be displayed in the textfield1 of button1 clicked and textField2 of button2 clicked ViewController. below is what i have tried as am learning, i don't know wheather it is correct or not.
CustomerVC.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
- (IBAction)button1Click:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *txtField1;
- (IBAction)button2Click:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *txtField2;
CustomerTableVC.h
#import <UIKit/UIKit.h>
#interface DetailsViewController : UITableViewController
#end
CustomerTableVC.m
#import "DetailsViewController.h"
#interface DetailsViewController ()
{
NSArray *array1,*array2;
}
#end
#implementation FamilyDetailsViewController
- (void)viewDidLoad {
[super viewDidLoad];
array1=[[NSArray alloc]initWithObjects:#"Apple",#"Samsung",#"Blackberry",#"Windows", nil];
array2=[[NSArray alloc]initWithObjects:#"Doctor",#"Engineer",#"Businessman",#"Employee", nil];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array1 count];
return [array2 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=[arrqy1 objectAtIndex:indexPath.row];
cell.textLabel.text=[arrqy2 objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.navigationController popViewControllerAnimated:YES];
}
You should use Unwind segue to pass data back from your tableview controller..
Steps to follow:
Suppose A & B are two controllers and you first navigated from A to B with some data. And now you want to POP from B to A with some data.
Unwind Segues is the best and recommended way to do this.
Here are the steps.
Open A.m
define following method
#IBAction func unwindSegueFromBtoA(segue: UIStoryNoardSegue) {
}
open storyboard
Select B ViewController and click on ViewController outlet. press control key and drag to 'Exit' outlet and leave mouse here. In below image, selected icon is ViewController outlet and the last one with Exit sign is Exit Outlet.
You will see 'unwindSegueFromBtoA' method in a popup . Select this method .
Now you will see a segue in your view controler hierarchy in left side. You will see your created segue near StoryBoard Entry Piont in following Image.
Select this and set an identifier to it. (suggest to set the same name as method - unwindSegueFromBtoA)
Open B.m . Now, wherever you want to pop to A. use
self.performSegueWithIdentifier("unwindSegueFromBtoA", sender: dataToSend)
Now when you will pop to 'A', 'unwindSegueFromBtoA' method will be called. In unwindSegueFromBtoA of 'A' you can access any object of 'B'.
That's it..!
Try using delegate.
1. Declare delegate on Tableview controller's.
In Custom Vc
Declare FamilyViewController.delegate = self;
self.view presentviewcontroller = familyviewController;
Define the delegate function (eg. detailsselected())
Inside the detailsSelected dismissViewController.
In FailyDetailsViewController:
Inside the didselectRowAtIndexPath : call the "detailsSelected" function and pass the selected values.
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.
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
This is a Q&A Style Post
If I want to pass parameters, such as an NSString, NSArray, or NSDictionary, from one view controller to another, what is the easiest way to do this?
There are multiple ways to achieve this, but two of the cleanest methods would involve passing the parameter(s) into a custom init method for the next view controller, or setting the parameter(s) as a property of the next view controller.
Note that this is not restricted to passing data between view controllers - view controllers are objects and any objects can use the following principles, I'm simply using view controllers for the following examples.
Both of these examples are using a simple UITableViewController as the initial view controller, and the next view controller will be passed the user's selection from a list of cells where they can choose their favorite color, as well as the current date of their selection. This can be done FROM any type of view controller TO any type of view controller with a few minor modifications, and within reason there's really no limit to the types/quantity of information that can be passed this way.
keep in mind that you likely don't want a massive initializer method with 10 parameter names, so in that case you might want to have individual properties and set each one accordingly. You also might want to keep the initialization/setup code to a single line if there's only a few parameters, so using a custom initializer method might be for you in that case.
Demo table view setup
#import "TestTableViewController.h"
#import "NextViewController.h"
#interface TestTableViewController ()
#end
#implementation TestTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.colors = #[#"Red", #"Orange", #"Yellow", #"Green"];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.colors.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ColorCell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"ColorCell"];
}
cell.textLabel.text = self.colors[indexPath.row];
return cell;
}
Method #1 - Custom Initializer
NextViewController.h
#import <UIKit/UIKit.h>
#interface NextViewController : UIViewController
// expose the custom initializer so other view controller's can pass in the parameters we want to pass
- (instancetype)initWithFavoriteColor:(NSString *)favoriteColor currentDate:(NSDate *)date;
#end
NextViewController.m
#import "NextViewController.h"
#implementation NextViewController
// implement the custom initializer method
- (instancetype)initWithFavoriteColor:(NSString *)favoriteColor currentDate:(NSDate *)date {
self = [super init];
// do whatever you want here with the favorite color string and current date that
// was passed in, such as save them to instance variables...
return self;
}
#end
Implement method #1 in our demo table view:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// get the color they selected for this row from our data source array
NSString *selectedColor = self.colors[indexPath.row];
// initialize the next view controller using the handy init method we created
NextViewController *nextVC = [[NextViewController alloc] initWithFavoriteColor:selectedColor currentDate:[NSDate date]];
[self.navigationController pushViewController:nextVC animated:YES];
}
Method #2 - Creating/Setting Properties
NextViewController.h
#import <UIKit/UIKit.h>
#interface NextViewController : UIViewController
#property (nonatomic, retain) NSString *favoriteColor;
#property (nonatomic, retain) NSDate *currentDate;
#end
NextViewController.m
#import "NextViewController.h"
#implementation NextViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do something here with your properties - by the time the view has loaded
// they will have been set/passed from the original view controller
self.favoriteColor...
self.currentDate...
}
#end
Implement method #2 in our demo table view:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// get the color they selected for this row from our data source array
NSString *selectedColor = self.colors[indexPath.row];
// initialize the next view controller normally, and set its favorite color property
// to be what the user selected
NextViewController *nextVC = [NextViewController new];
nextVC.favoriteColor = selectedColor;
nextVC.currentDate = [NSDate date];
[self.navigationController pushViewController:nextVC animated:YES];
}
In both instances, you have now quickly and easily passed multiple pieces of information from one view controller to another.
I'm fairly new to iOS development and I've been wrestling with a solution for this for about a day now and can't figure out why it is not working. I am trying to use a tableview within a viewcontroller as a small menu for the user to use. I have checked to see if the NSArray is being populated, and it is. I have also checked to see if the cell is being created, and it is. I just can't figure why it is not populating the tableview with the cells it creates. Below is my the code that I have so far. Thanks in advance for any help anyone can provide.
MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *menuTableView;
#property (weak, nonatomic) IBOutlet UIButton *menuButton;
#property (strong, nonatomic) NSArray *menuItemsArray;
#property (weak, nonatomic) IBOutlet UILabel *menuLabel;
#end
MainViewController.m
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize menuItemsArray, menuTableView, menuButton, menuLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Set TableView Delegate/DataSource to self
[self.menuTableView setDelegate:self];
[self.menuTableView setDataSource:self];
[self.menuTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.menuTableView setBounces:NO];
[self.menuTableView setRowHeight:self.menuLabel.frame.size.height];
self.menuItemsArray = [[NSArray alloc] initWithObjects:#"Add Category", #"Add Item", #"Settings", nil];
NSLog(#"array: %#", menuItemsArray);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#pragma mark - UITableViewDelegate
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return ([self.menuItemsArray count]);
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"menuCell"];
[[cell textLabel]setText:[self.menuItemsArray objectAtIndex:indexPath.row]];
[[cell textLabel]setFont:[self.menuLabel font]];
return cell;
}
-(void)tableView:(UITableView *)tableview didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.menuTableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *selectedString = [self.menuItemsArray objectAtIndex:indexPath.row];
self.menuLabel.text = selectedString;
}
I had the same problem, my table view was not getting displayed within a view controller.
I have found a solution.
You can create another view controller with a Container view on it. And put ur table view on a Table View controller. just embed the table view controller to the container view of ur mail view controller.
Make sure your initWithNib method is being called. If you are calling [[MainController alloc] init] your "menuTableView" will never be created from the Nib. Also, double-check the table view by setting the backgroundColor of the main table view to [UIColor red] or something just to make sure the tableView is present and that it has the frame you expect. It might be sitting behind one of your other views, have a frame of (0,0,0,0), or not be present in the view at all.
Also try calling [self.menuTableView reloadData] at the end of your 'viewDidLoad' or initialize the menuItemsArray before you set the data source and delegate (i.e. in your initWithNib method).
And when you do get it all working (you are very close) you will want to change your cellForRow method to something more like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:#"menuCell"];
if(!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"menuCell"];
}
[[cell textLabel]setText:[self.menuItemsArray objectAtIndex:indexPath.row]];
[[cell textLabel]setFont:[self.menuLabel font]];
return cell;
}
This will allow you to take advantage of the cell reuse that makes table views so efficient.
This is a bit late as you have found a way around it but I was having the same problem as you and found that I needed to connect the IBOutlet property to the table view in storyboard and then it all worked.
I hope this helps you in future.
One cause of the symptoms described is if you have placed the UITableView in the parent view using a container view in a storyboard, but are initialising and populating in code a different instance of the UITableView than the one that is actually being presented to the user. If you have placed the UITableView within the view using a container view, then you need to do the following:
Connect the UITableView to the container view with a segue, by Control-Dragging from the container view to the UITableView in the Storyboard.
Click on the segue, and give it a name e.g. tableViewSegue.
Set up the table by implementing prepareForSegue:sender:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString * segueName = segue.identifier;
if ([segueName isEqualToString: #"tableViewSegue"]) {
UIViewController * myTableView = [segue destinationViewController];
// Do any table setup here, such as injecting the data array into a property on the tableView.
}
}
If instead you have been creating a different UITableView in code, what you will see is an unpopulated UITableView that follows the specifications set up in the storyboard (e.g., row height spacing will be correct) and which is responding to user interaction, but is empty. The empty one is the one being initialised automatically for you by the storyboard, and meanwhile you've been creating another UITableView somewhere else:
// DON'T DO IT THIS WAY IF YOU'RE USING STORYBOARD.
- (void)viewDidLoad {
[super viewDidLoad];
// Incorrectly creating a tableview child table view that won't be the one presented.
self.myTableView = [MYTableViewClass new];
// ...further configuration of the table.
}
If you follow this incorrect path, the other UITableView you are creating is being built in memory, and populated with your data array, so you will see all the NSLog statements from that process and be able to see a UITableView in memory with the correct number of objects and so on as you step through the executing code, but what is hard to pick up is you're not looking at the one being presented to the user. So can be tricky to track down. :)
Just remove the code above, implement prepareForSegue:sender: and the universe will return to being a predictable place.
If you add UITableView inside the UIViewController, you need to set the frame size of the UITableView same as the frame size of the view inside the UIViewController, otherwise the tableview size may be 0, cannot display anything.
You can set the frame size if you create the UITableView by storyboard in your case:
- (void)viewDidLoad {
[super viewDidLoad];
// Set tableview delegate and datasource here
menuTableView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
I have a class like this:
#interface ExerciseLogDetails : UIViewController<UIActionSheetDelegate, UITableViewDelegate, UITableViewDataSource> {
where I am trying to display some elements followed by a UITextView. The UITextView element is created on Interface Builder. When executing this code:
- (void)viewDidLoad {
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:self.tableView];
}
a table shows, but not the one I configured in Interface Builder. It is completely blank and unformatted. How can I access my table and populate it progrmmatically with data?
Thank you!
Several of the tips on this thread helped me create this. I am going to offer some more complete code files in order to help others as well:
Step 1. Drag your UITableView onto your View Controller either in Storyboards or XIBs. In my example I am using a story board.
Step 2: Open your ViewController (in my case its just DefaultViewController) and add the two delegates for the UITableView: UITableViewDelegate and UITableViewDataSource. Also add a simple data source for population and the UITableView IBOutlet.
DefaultViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) NSMutableArray *newsArray;
#end
Step 3: Open your implementation file (DefaultViewController.m) and add the following:
#import "DetailViewController.h"
#interface DetailViewController ()
- (void)configureView;
#end
#implementation DetailViewController
#synthesize newsArray;
#synthesize tableView;
#pragma mark - Managing the detail item
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)configureView
{
// Update the user interface for the detail item.
self.newsArray = [[NSMutableArray alloc] initWithObjects:#"Hello World",#"Goodbye World", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// typically you need know which item the user has selected.
// this method allows you to keep track of the selection
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
// This will tell your UITableView how many rows you wish to have in each section.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.newsArray count];
}
// This will tell your UITableView what data to put in which cells in your table.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifer = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
// Using a cell identifier will allow your app to reuse cells as they come and go from the screen.
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
// Deciding which data to put into this particular cell.
// If it the first row, the data input will be "Data1" from the array.
NSUInteger row = [indexPath row];
cell.textLabel.text = [self.newsArray objectAtIndex:row];
return cell;
}
#end
Step 4: Goto your Storyboards or XIB and select your UITableView and drag the datasource and delegate outlets onto your DefaultViewController to wire them up. Also you will need to wire up the Referencing Outlet for the UITableView to your IBOutlet tableView object you created in your header file.
Once this is finished you should be able to run it and the sample data will be in place.
I hope this along with the other tips on this thread will help others setup a UITableView from scratch on a ViewController.
If you configured a tableView in IB you shouldn't also create one programmatically, you should create #property (nonatomic, retain) IBOutlet UITableView *tableView; and connect it to the tableView you configured in IB.
Try to set a breakpoint in the tableView's
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
delegate method to see if this method get called.
From Apple UITableView docs:
A UITableView object must have an object that acts as a data source
and an object that acts as a delegate; typically these objects are
either the application delegate or, more frequently, a custom
UITableViewController object. The data source must adopt the
UITableViewDataSource protocol and the delegate must adopt the
UITableViewDelegate protocol. The data source provides information
that UITableView needs to construct tables and manages the data model
when rows of a table are inserted, deleted, or reordered. The delegate
provides the cells used by tables and performs other tasks, such as
managing accessory views and selections.
As u can see if u don't set a dataSource to your tableView, the tableView will not know how and what to display, so nothing will happen.
You can set one by calling tableView.dataSource = self; or in IB drag from your tableView to the file's owner (that is your viewController that must implement the UITableViewDataSource Protocol)
There are two methods in the UITableViewDataSource protocol that your dataSource must implement:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
If u won't implement those methods u will get a compiler warnings.
You can have more control on how the tableView will look if you implement the UITableViewDelegate protocol - like row/header/footer height, selections and more...
From Apple UITableView docs:
UITableView overrides the layoutSubviews method of UIView so that it
calls reloadData only when you create a new instance of UITableView or
when you assign a new data source. Reloading the table view clears
current state, including the current selection. However, if you
explicitly call reloadData, it clears this state and any subsequent
direct or indirect call to layoutSubviews does not trigger a reload.
ReloadData get called when the tableView is created or when you assign a new dataSource (or when you explicitly call it of course..).
This is when the tableView needs to know what to display (how many sections?, how many rows?, and which cell to display?) - So this is when numberOfRowsInSextion method called.
Like Eyal said, you shouldn't create a UITableView programmatically and in the Interface Builder. Instead, it is much easier to just create one in Interface Builder and assigns it's delegate and datasource properties to File's Owner in IB.
Once you've done this, you don't need to create one programmatically and there's no need for a #property for the tableview.
Instead, you could have your UIViewController's class files look like this:
// YourViewController.h
#import <UIKit/UIKit.h>
#interface YourViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) NSArray *yourData;
#end
Where the NSArray will contain your data that you will enter into the table programmatically. You may use other data classes too like an NSDictionary depending on what data you have and how you want it to sit in the table.
// YourViewController.m
#import "YourViewController.h"
#implementation YourViewController
#synthesize yourData;
- (void)viewDidLoad
{
[super viewDidLoad];
// Here you are creating some data to go in your table by inputting it as an array.
// I just used some basic strings as an example.
NSArray *array = [[NSArray alloc] initWithObjects:#"Data1", #"Data2", #"Data3", nil];
// Copying the array you just created to your data array for use in your table.
self.yourData = array;
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.yourData = nil;
}
#pragma mark Table View Data Source Methods
// This will tell your UITableView how many rows you wish to have in each section.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.yourData count];
}
// This will tell your UITableView what data to put in which cells in your table.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifer = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
// Using a cell identifier will allow your app to reuse cells as they come and go from the screen.
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
// Deciding which data to put into this particular cell.
// If it the first row, the data input will be "Data1" from the array.
NSUInteger row = [indexPath row];
cell.textLabel.text = [yourData objectAtIndex:row];
return cell;
}
#end
This should just create a simple UITableView with three entries of data that you have entered programmatically.
If you have any problems or questions just post a comment. :)
Hope this helps.