Displaying an URL from a different view controller in a UIWebView - ios

RecipesTableViewController.m
#import "RecipesTableViewController.h"
#import "RecipeTableViewCell.h"
#import "IngredientsViewController.h"
#import "Recipe.h"
#import "RecipeDetailViewController.h"
#interface RecipesTableViewController () {
NSMutableArray *recipesArray;
}
#end
#implementation RecipesTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
//api from recipepuppy
NSString *recipeUrlString = [NSString stringWithFormat:#"http://www.recipepuppy.com/api/?i=%#",self.searchRecipe];
//adding percentage on the textfield when the user is searching
NSString *formattedString = [recipeUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//download data
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString: formattedString]];
//put data into a dictionary
NSDictionary *recipeDictinary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
//then put the dictionary into an array
recipesArray = [[NSMutableArray alloc]init];
for (NSDictionary *recipeDict in [recipeDictinary objectForKey:#"results"]) {
Recipe *recipe = [[Recipe alloc]initWithTitle:[recipeDict objectForKey:#"title"] andRecipeIngredients:[recipeDict objectForKey:#"ingredients"] andImageURL:[NSURL URLWithString:[recipeDict objectForKey:#"thumbnail"]] andRecipeWebUrl:[recipeDict objectForKey:#"href"]];
[recipesArray addObject:recipe];
NSLog(#"%#", recipeDict);
}
}
#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 [recipesArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RecipeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"recipeCell" forIndexPath:indexPath];
[cell drawTheCell:[recipesArray objectAtIndex:indexPath.row]];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"recipeDetail"]) {
//NSIndexPath *indexPath =[self.tableView indexPathForSelectedRow];
RecipeDetailViewController *recipeDetail = segue.destinationViewController;
recipeDetail.title = #"Recipe";
}
}
#end
Short Story:
I’m making a recipe by ingredient for my class.
I have a UITableViewControllre parsing content from an api and and I have the objects of the api in an array. In that array I have “results” and in those results I have urls, title, ingredients, and image of recipe. I want to send the url to a WebView into another view controller but I just can’t. Whenever I select the recipe the app crashes to view the webview. I been stuck on this for threes days and I’m so frustrated and I know the problem is my linking to the webview because the array prints the url but is not displayed on the webview.
this is my table view controller where my api is and the prepare for segue to the view controller where the webview is at.
RecipeTableViewCell.m
#import <UIKit/UIKit.h>
#import "Recipe.h"
#interface RecipeTableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *recipeUrl;
#property (strong, nonatomic) IBOutlet UILabel *recipeTitle;
#property (strong, nonatomic) IBOutlet UILabel *recipeIngredients;
#property (strong, nonatomic) IBOutlet UIImageView *recipeImage;
-(void)drawTheCell:(Recipe *)recipeObject;
#end
RecipeTableViewCell.m
-(void)drawTheCell:(Recipe *)recipeObject {
self.recipeTitle.text = recipeObject.title;
self.recipeIngredients.text = recipeObject.ingredients;
self.recipeUrl.text = recipeObject.recipeWebUrl;
NSData *imageData = [NSData dataWithContentsOfURL:recipeObject.imageURL];
self.recipeImage.image = [UIImage imageWithData:imageData];
#import "RecipeDetailViewController.h"
#import "RecipeTableViewCell.h"
#interface RecipeDetailViewController ()
#property (strong, nonatomic) IBOutlet UIWebView *recipeWebView;
#end
RecipeDetailViewController.m
#implementation RecipeDetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
Recipe *recipe = [[Recipe alloc] init];
NSURL *url = [NSURL URLWithString: recipe.recipeWebUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.recipeWebView loadRequest:request];
}
RecipeDetailViewController.h
#import <UIKit/UIKit.h>
#interface RecipeDetailViewController : UIViewController
#property (nonatomic, strong ) NSString *recipeWebUrlString;
this is my cell and in here the title, ingredients, and image is displayed and it works fine.

Skyler's answer's heading in the right direction, but it's missing a few critical pieces...
Yes, you need to pass the web url string in prepareForSegue: like he's suggesting, i.e.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"recipeDetail"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeTableViewCell *cell = (RecipeTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath];
RecipeDetailViewController *recipeDetail = segue.destinationViewController;
recipeDetail.title = #"Recipe";
recipeDetail.recipeWebUrlString = cell.recipeUrl.text;
}
}
but the problem is that you're not using that recipeWebUrlString to perform your url request.
Instead, you're creating an empty Recipe object in your .m and thus using an empty url to perform the web request, i.e.
Recipe *recipe = [[Recipe alloc] init];
NSURL *url = [NSURL URLWithString: recipe.recipeWebUrl];
Instead replace those two lines (^) with the following:
NSURL *url = [NSURL URLWithString:self.recipeWebUrlString];
in order to use the url you just passed in from the RecipesTableViewController.

There is a long winded answer here but I will try to keep it short and hopefully it makes sense.
First I don't see where you are calling performSegueWithIdentifier: which means you are likely doing a segue from clicking the cell directly to the next view via storyboard. This is great for a simple button press but not ideal for selecting a cell that you need to send information from. I would recommend calling the segue in didSelectRowAtIndexPath:. The segue on the storyboard should go directly from one view controller to another and not directly from a table cell. Don't forget to set the identifier again.
Something like this to call the segue in code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"recipeDetail" sender:nil]; //you could also pass the cell with if you want
}
Second in your prepare for segue you are not setting the URL you want and just setting the title of the next view controller. Looks like you were close to what you wanted because I can see you were already looking at the index path but commented it out. You should grab the cell there and set the url to the recipeDetail. You could also pass the cell via sender.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"recipeDetail"])
{
NSIndexPath *indexPath =[self.tableView indexPathForSelectedRow];
RecipeTableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
RecipeDetailViewController *recipeDetail = segue.destinationViewController;
recipeDetail.title = #"Recipe";
recipeDetail.recipeWebUrlString = cell. recipeUrl.text;
}
}
Third thing if all else fails start putting NSLogs everywhere. You can log the URL in the view did load in the next view and see that it isn't getting set. The next thing you should have been looking at where you are setting it, which appears to be no where as far as I can see =)
Also with all of that being said I would not relay on the text on the cell but instead grab the recipe from your array and pass that in the segue.
I hope that helps or at least gets you pointed in the right direction.

Related

IOS Obj-C ViewController not passing value

I know this question has been asked, but I have read the responses, adopted the fixes and still have an issue somewhere which I can't identify.
I have an IOS app which is similar in nature to email. It has an InboxVC which is a tableVC with a custom prototype cell which upon selection triggers a messageDetailVC.
The issue is that the messageDetailVC is triggered but the values are not being passed to it. I added log messages to evaluate the value in code before getting to Storyboard issues, and the variable being passed (messageID) has a NULL value.
Can someone tell me what I am missing or doing wrong? My code is:
InboxVC.m (snippet)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(#"%#: Message touched...", LOG_TAG);
messageDetail *mdvc = [[messageDetail alloc] init];
mdvc.messageID = #"123456789-1";
[self.navigationController pushViewController:mdvc animated:YES];
NSLog(#"%#: messageID value is = %#",LOG_TAG,mdvc.messageID);
//messageID has valid value here
}
messageDetail.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface messageDetail : UIViewController
{
NSString *messageID;
}
#property (strong, nonatomic) NSString *messageID;
#property (strong, nonatomic) IBOutlet UITextView *body;
messageDetail.m
#import <Foundation/Foundation.h>
#import "messageDetail.h"
static NSString * LOG_TAG = #"messageDetailController";
#implementation messageDetail
#synthesize messageID;
#synthesize body;
- (void)viewDidLoad {
[super viewDidLoad];
body.text = messageID;
//debug messages
NSLog(#"%#:Added messageID as Body text", LOG_TAG);
NSLog(#"%#:Value of body.text is = %#", LOG_TAG, body.text);
NSLog(#"%#:Value of messageID is = %#", LOG_TAG, messageID);
//messageID has null value here
}
- (void) viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO];
}
MainStoryboard:
I have a messageDetail view tied to the messageDetail sub class of UIViewController. There is a segue with identifier "s_msgDetail" from the prototype cell's selection event to the messageDetailVC (show). As I said above the messageDetailVC appears, just not with the body.text being set to the value of "messageID".
You are setting the messageID on the wrong instance of your messageDetail view controller. Since you are using storyboards, the storyboard is already creating and presenting an instance of messageDetail for you. But, in tableView:didSelectRowAtIndexPath: you are creating a second one and presenting it. (I'm surprised you're not getting a bunch of runtime animation warnings when doing that.)
You should be setting messageID in prepareForSegue:sender: like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"s_msgDetail"]) {
messageDetail * vc = (messageDetail *) segue.destinationViewController;
NSIndexPath * indexPath = [self.tableView indexPathForSelectedRow];
vc.messageID = /* use indexPath to get data from messages collection */;
}
}
You have to use prepareForSegue to pass value to another view Controller

How to change tableview data with button press on a different view controller?

I am having a bit of trouble getting a button pressed on a ViewController to load a specific lot of information in an array for displaying on a UITableViewController.
I know you can do it with detecting the segue but I have tried this without - I had success. I read somewhere else how I can use tags on the button to identify it and I tried to use this but it only displays one set of data no matter what button I have pressed. I've tried swapping a lot of things around to try and get it to work but to no avail. I have a ViewController with the buttons on it, a TableViewController to display the arrays and a CellViewController to display the better description of the tableview info.
My code is pretty 'all over the shop' at the moment. I haven't included the Cell View controllers as I don't think they have that much relevance with what I want to do at this stage.The buttonviewcontroller is just a normal UIViewController and both buttons are linked as 'Button1'class in the storyboard. They have been given tags 0 and 1 which I believe is in under the 'view' subheading in the storyboard. Please alert me if this is wrong and I am missing something really obvious but that was the only place I could find 'tag'.
Honestly I don't know exactly what I'm doing in xcode as I'm not very familiar with it but seem to understanding parts of it. However, I'm not sure exactly what I'm doing wrong at the moment.
Code:
ButtonViewController.M (this is where the buttons are)
#import <UIKit/UIKit.h>
#import "tableViewController.h"
#import "TableCell.h"
#interface ButtonViewController : UIViewController
-(IBAction) button_Clicked:(id)sender;
#property (strong, nonatomic) IBOutlet UIButton *Button1;
#end
ButtonViewController.m
#import "ButtonViewController.h"
#import "tableViewController.h"
#import "TableCell.h"
#interface ButtonViewController ()
#end
#implementation ButtonViewController
-(IBAction) button_Clicked:(id)sender
{
//something here that is going wrong
tableViewController *tableVC = [[tableViewController alloc]initWithNibName:#"tableViewController" bundle:nil];
if(_Button1.tag==0)
{
tableVC.buttonSelected = 0;
}
else if(_Button1.tag==1)
{
tableVC.buttonSelected = 1;
}
[self.navigationController pushViewController:tableVC animated:YES];
[tableVC.tableView reloadData];
#end
tableViewController.h
#import <UIKit/UIKit.h>
#interface tableViewController : UITableViewController
#property (nonatomic, assign) int buttonSelected;
#property (nonatomic, strong) NSArray *Title;
//#property (nonatomic, strong) NSMutableArray *Title;
#property (nonatomic, strong) NSArray *Description;
//#property (nonatomic, strong) NSMutableArray *Description;
//Not sure if Mutable or normal array
#end
tableViewController.m
#import "tableViewController.h"
#import "TableCell.h"
#import "DetailViewController.h"
#import "ButtonViewController.h"
#interface tableViewController ()
#end
#implementation tableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if(_buttonSelected == 0)
{
_Title = #[#"Hamstring Muscle Tear",#"Lower Back Pain"];
_Description = #[#"Blahahaha", #"blahahaha2",#"blalalala3"];
[self.tableView reloadData];
}
else if (_buttonSelected == 1)
{
_Title = #[#"1",#"2",#"3"];
_Description = #[#"dededdededde", #"deddedede2",#"blalalala3"];
[self.tableView reloadData];
}
#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 _Title.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TableCell";
TableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _Description[row];
return cell;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowDetails"]) {
DetailViewController *detailviewcontroller = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
int row = [myIndexPath row];
detailviewcontroller.DetailModal = #[_Title[row],_Description[row]];
}
}
#end
There are two ways to doing same.
Implement delegate method and call it on event.
You can fire NSPostNotification event from one controller to another controller.
Change the data, then post an NSNotification that it changed.
The table view data source observes the notification and calls [tableView reloadData]
You can find details how to do this in apple's sample code.

How can I link a button in iOS to change an array for a list view?

Not sure if the above is even my question as I am basically new to using objective-c and iOS and not sure if that's the right wording but anyway...
Basically I have completed this tutorial https://www.youtube.com/watch?v=ITUI7fukRO8 for using a tableView with an array of data.
The substance of the program consists of a navcontroller, tableviewController and a viewController for the extra detail of the table data.
Currently I have a button (from another viewcontroller) that is linking to my tableView.
How would I go about setting up and IF statement in regards to the button pressed (-as I need to link other buttons from other controllers) so that if a specific button is pressed, the array that is displayed in the tableView is different.
I'm currently thinking of doing a bunch of if, then, else statements and just manually changing the detail of the arrays.
Is this the way to go??
Or should I just set up another table view for each of my buttons.
I have a set of .h and .m for the tableCell, tableCellController, and the ViewController.
That probably didn't make a whole lot of sense but if you somewhat understand help would be appreciated.
Cheers
Edit: As you can see my code is pretty 'all over the shop' at the moment. I haven't included the Cell View controllers as I don't think they have that much relevance with what I want to do at this stage.The buttonviewcontroller is just a normal UIViewController and both buttons are linked as 'Button1'class in the storyboard. They have been given tags 0 and 1 which I believe is in under the 'view' subheading in the storyboard. Please alert me if this is wrong and I am missing something really obvious but that was the only place I could find 'tag'. ARRRG Objective-c is frustrating when you don't the language at all./
Updated Code as 21/03.
ButtonViewController.M (this is where the buttons are)
#import <UIKit/UIKit.h>
#import "tableViewController.h"
#import "TableCell.h"
#interface ButtonViewController : UIViewController
-(IBAction) button_Clicked:(id)sender;
#property (strong, nonatomic) IBOutlet UIButton *Button1;
#end
ButtonViewController.m
#import "ButtonViewController.h"
#import "tableViewController.h"
#import "TableCell.h"
#interface ButtonViewController ()
#end
#implementation ButtonViewController
-(IBAction) button_Clicked:(id)sender
{
//something here that is going wrong
tableViewController *tableVC = [[tableViewController alloc]initWithNibName:#"tableViewController" bundle:nil];
if(_Button1.tag==0)
{
tableVC.buttonSelected = 0;
}
else if(_Button1.tag==1)
{
tableVC.buttonSelected = 1;
}
[self.navigationController pushViewController:tableVC animated:YES];
[tableVC.tableView reloadData];
#end
tableViewController.h
#import <UIKit/UIKit.h>
#interface tableViewController : UITableViewController
#property (nonatomic, assign) int buttonSelected;
#property (nonatomic, strong) NSArray *Title;
//#property (nonatomic, strong) NSMutableArray *Title;
#property (nonatomic, strong) NSArray *Description;
//#property (nonatomic, strong) NSMutableArray *Description;
//Not sure if Mutable or normal array
#end
tableViewController.m
#import "tableViewController.h"
#import "TableCell.h"
#import "DetailViewController.h"
#import "ButtonViewController.h"
#interface tableViewController ()
#end
#implementation tableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if(_buttonSelected == 0)
{
_Title = #[#"Hamstring Muscle Tear",#"Lower Back Pain"];
_Description = #[#"Blahahaha", #"blahahaha2",#"blalalala3"];
[self.tableView reloadData];
}
else if (_buttonSelected == 1)
{
_Title = #[#"1",#"2",#"3"];
_Description = #[#"dededdededde", #"deddedede2",#"blalalala3"];
[self.tableView reloadData];
}
#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 _Title.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TableCell";
TableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _Description[row];
return cell;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowDetails"]) {
DetailViewController *detailviewcontroller = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
int row = [myIndexPath row];
detailviewcontroller.DetailModal = #[_Title[row],_Description[row]];
}
}
#end
Do a CTRL - drag in Storyboard to create a IBAction for the button.
In the IBAction method, get the data you want for your data arrays (Images, Title, Description ref the tutorial you linked). Then call:
[tableview reloadData]
This will repopulate the tableview.

"Attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update" Error

I have an app that is selecting a person from their contacts list and takes their First name, last name and email. It then saves the first name to a nsmutablearray and puts it into a uitableview cell. My problem occurs once the contact is selected in the simulator.
Code:
.h:
#import <UIKit/UIKit.h>
#import <AddressBookUI/AddressBookUI.h>
#interface FirstViewController : UIViewController < ABPeoplePickerNavigationControllerDelegate, UITableViewDelegate, UITableViewDataSource>
- (IBAction)showPicker:(id)sender;
#property (weak, nonatomic) IBOutlet NSString *firstName;
#property (weak, nonatomic) IBOutlet NSString *email;
#property (weak, nonatomic) IBOutlet NSString *lastName;
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (strong, nonatomic) NSMutableArray *contacts;
#end
.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize firstName;
#synthesize email;
#synthesize lastName;
#synthesize contacts;
#synthesize myTableView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
contacts = [[NSMutableArray alloc]initWithObjects:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - UITableView Datasource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return contacts.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 = [contacts objectAtIndex:indexPath.row];
return cell;
}
- (IBAction)showPicker:(id)sender {
ABPeoplePickerNavigationController *picker =
[[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
}
- (void)peoplePickerNavigationControllerDidCancel:
(ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
[self displayPerson:person];
[self dismissModalViewControllerAnimated:YES];
return NO;
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
return NO;
}
- (void)displayPerson:(ABRecordRef)person
{
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person,
kABPersonFirstNameProperty);
self.firstName = name;
NSString* last = (__bridge_transfer NSString*)ABRecordCopyValue(person,
kABPersonLastNameProperty);
self.lastName = last;
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
NSString *emailId = (__bridge NSString *)ABMultiValueCopyValueAtIndex(emails, 0);//0 for "Home Email" and 1 for "Work Email".
self.email = emailId;
if (!(contacts))
{
contacts = [[NSMutableArray alloc]init];
}
[contacts insertObject:firstName atIndex:0];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.myTableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#end
UITableView must be kept in sync with the data source at all times. Special care must be taken if the data source can change in a background thread.
When something is added to the data source, call beginUpdate/insert/endUpdate as soon as possible. You don't have to worry about caching these, the UITableView will cache changes to be executed when it determines there is enough cpu time and resources.
The moment endUpdates is called, the UITable will ask the dataSource for the number of sections and rows again. If your number of sections and row feeds directly from the dataSource, then number sections and rows, plus insertions, minus deletions must equal the numbers returned by the end calls for numberOfSections and numberOfRowsInSection.
One last tip: avoid mixing calls to 'reloadData' and beginUpdate/endUpdate pairs. Use one or the other, not both.
I have encountered same problem as this. All you have to do is change
[self.myTableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationAutomatic];
to
[self.myTableView beginUpdates];
[self.myTableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationAutomatic];
[self.myTableView endUpdates];
From UITableView Documentation
beginUpdates
Begin a series of method calls that insert, delete, or select rows and sections of the receiver.
When you use beginUpdates, you must call endUpdates and not reloadData.
You can check this link for more UITableView information.
The comments above about implementing -tableView:numberOfRowsInSection: are correct. The assertion is checking the returned value expecting it will increase.
However since you didn't set your dataSource on the UITableView its calling (or not calling) a method that doesn't exist and getting 0.
You need to set the myTableView.dataSource and (since you also implement the delegate protocol) myTableView.delegate to self.
You're also likely to need something like
- (void)viewDidLoad
{
[super viewDidLoad];
[self.myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
...
}
Unless you're registering that somewhere else or your storyboard has a "Prototype Cell" with the identifier "Cell" which your code asks for.
I find this problem commonly occurs when I am placing a table view inside of a View Controller. If you're using a UITableViewController jump to 3.
These steps may help:
1: In your View Controller .h file make sure you add the following:
#interface YourViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
2: Next create an IBOutlet for your table view by ctrl + drag to your .h class. It should look like:
#property (weak, nonatomic) IBOutlet UITableView *tableView;
3: Next step is to ctrl + drag to your View Controllers icon (see image)
You need to do this twice selecting:
- delegate
- datasource
Finally, in your .m file, you should have the following method:
- (void) viewWillAppear:(BOOL)animated{
//[self.tableView beginUpdates];
//[self.tableView endUpdates];
[self.tableView reloadData];
}
You can use either beginUpDates/endUpdates or reloadData, however Apple docs recommend reloadData.
Once done your table should work fine.
You need to maintain count for contacts array and increment accordingly.
and while creating indexPath you need to set appropriate indexPathForRow: and section count(if required).
- (void)displayPerson:(ABRecordRef)person
{
..........
[contacts insertObject:firstName atIndex:0];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; // you cant do this increment appropriately
.........
}
Please check your tableview datasource and delegate methods. You may be passing empty array to datasource methods. And don't forget to reload tableview after getting data.
I know it's stupid - I got the same error because I forgot to set the delegate and dataSource.
So after inserting rows and doing tableView.endUpdates() the tableView thought it must have some rows - but due to the unlinked dataSource, it has not.

Passing information between viewControllers in Storyboard

I am learning how to use storyboards by making a very simple app. On the main view controller (InfoViewController), I have a textField by the name: nameField. After entering text in this field, when I enter the save button, the text should should get appended to the array (list) (declared in TableViewController) and be displayed on the table in TableViewController.
Also, the segue identifier is: GoToTableViewController.
However, the text does not get passed from nameField to the list (array). At first, I assumed that I was making some mistake with the textField. So I replaced it with a static text. But that did not help either. Then I checked if the string has been added to the array by using NSLog() , but every time I get Null. From my understanding, the list (array) is not created until TableViewController is loaded. For that reason, I alloc and init list in InfoViewController. But it does not help.
Can somebody please help me find out the mistake that I am making?
Thanks!
Relevant sections of my code are as follows:
InfoViewController.h
#import <UIKit/UIKit.h>
#class TableViewController;
#interface InfoViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *nameField;
#property (strong, nonatomic) TableViewController *tableViewController;
#end
InfoViewController.m
#import "InfoViewController.h"
#import "TableViewController.h"
#implementation InfoViewController
#synthesize nameField;
#synthesize tableViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
tableViewController = [[TableViewController alloc] init];
tableViewController.list = [[NSMutableArray alloc] init];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqual:#"GoToTableViewController"])
{
/* Pass data to list and then reloadTable data */
tableViewController = segue.destinationViewController;
tableViewController.infoViewController = self;
// (*) [tableViewController.list addObject:nameField.text];
// (*) [tableViewController.list addObject:#"Hi!"];
[tableViewController.list insertObject:#"Hi" atIndex:0];
// (**) NSLog(#"%#", [tableViewController.list objectAtIndex:0]);
[tableViewController.tableView reloadData];
}
}
#end
( * ) I inserted these statements to see if I was making a mistake with using the value in nameField.
( ** ) This statement is meant to check the value inserted in the array.
TableViewController.h
#import <UIKit/UIKit.h>
#class InfoViewController;
#interface TableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *list;
#property (strong, nonatomic) InfoViewController *infoViewController;
#end
TableViewController.m
#import "TableViewController.h"
#import "InfoViewController.h"
#implementation TableViewController
#synthesize list;
#synthesize infoViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
list = [[NSMutableArray alloc] init];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{ return 1; }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ return list.count; }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [list objectAtIndex:indexPath.row];
return cell;
}
#end
Reload the table in viewWillAppear method of tableViewController:
[tableViewController.tableView reloadData];

Resources