Can't reload Table View in tab bar controller - objective-c

Hi I have a tab tab controller and my first tab includes a view with:
3 text fields
a submit button
a tableView
Once I fill in the text fields I click submit and it adds the information to my managedObjectContext which is an sqlite database (CoreData).
As soon as I click submit I want the tableView to reload to include the added object. Currently my tableView will display the data in the database but it will only add the new row when I stop and re-run the simulator
This is the code for when the add button is tapped, it is here that I can't get the reload tableView working because it says tableView is an undeclared identifier, what have i missed?
-(IBAction)addButtonTapped:(id)sender {
NSLog (#"Add Button Tapped");
NSLog(#"Adding %# units of item code %# at $%# each",quantityTextField.text,productTextField.text,priceTextField.text);
Products_MarketAppDelegate* delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext* managedObjectContext = delegate.managedObjectContext;
NSManagedObject* newProduct;
newProduct = [NSEntityDescription insertNewObjectForEntityForName:#"Product" inManagedObjectContext:managedObjectContext];
[newProduct setValue:productTextField.text forKey:#"itemCode"];
[newProduct setValue:quantityTextField.text forKey:#"quantity"];
[newProduct setValue:priceTextField.text forKey:#"price"];
if ([managedObjectContext hasChanges])
NSLog(#"Managed Object Changed");
NSError* error;
[managedObjectContext save:&error];
// Insert Reload Table Code Here
// ** I have tried the following and it gives an error "Use of undeclared identifier 'tableView'"
//[tableView reloadData];
//[self.tableView reloadData];
}
As you can see below I have added the UITableViewDelegate & UITableViewDataSource in the header file. I have also hooked up the tableview in IB so that the delegate and datasource connections are linked to file's owner.
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface FirstViewController : UIViewController
<UIApplicationDelegate, UITableViewDataSource,UITableViewDelegate,NSFetchedResultsControllerDelegate>
{
IBOutlet UITextField *productTextField;
IBOutlet UITextField *quantityTextField;
IBOutlet UITextField *priceTextField;
NSMutableArray *items;
NSFetchedResultsController *fetchedResultsController;
}
#property (nonatomic, retain) NSMutableArray *items;
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
-(IBAction)addButtonTapped:(id)sender;
#end
This is the code to fill the tableView which works correctly
#pragma mark TableView
-(NSInteger)numberOfSectionsInTableView: (UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (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] autorelease];
}
// Configure the cell
Product* productItem =[fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%# x %# # $%#",productItem.quantity,productItem.itemCode,productItem.price];
return cell;
}
I have searched for answers on this site and on others but I must be doing something different and the solutions aren't helping me

Your UIViewController does not currently have an instance variable pointing to your tableview. Set one up:
#property (nonatomic, retain) IBOutlet UITableView *myTableView;
Remember to synthesize this in your .m
#synthesize myTableView;
Then in your code you can call
[self.myTableView reloadData];
You might have got confused by looking at code examples that use a UITableViewController instead of a UIViewController. The UITableViewController already has an instance variable called tableView, so your subclass wouldn't need it's own tableView instance variable declared. But you're using a UIViewController, so you must declare a tableView instance variable.

Thanks #MattyG for all your help. At first I wasn't sure if I was going against the norm and thats why it wasn't working.
I ended up solving the problem due to your suggestions & it works perfectly! I used the debugger and found that that although we had created a property for the table I had not created an IBOutlet and linked it in my nib file with:
IBOutlet UITableView *myTableView;
I guess this meant that I was telling myTableView to reload but it wasn't hooked up to my table and thus couldn't use the datasource methods.

Related

Not able to obtain correct indexPath.row value from objective C file in Swift file

File: ContactsViewController.m
In this file I am using the didSelectRowAtIndexPath method to push a new View Controller to show information about the name that was pressed on the Table View Controller. The View Controller that will be displaying the information about the name is being implemented in Swift. The part that I am referring to in this code is in the didSelectRowAtIndexPath method, line:
_myIndex = indexPath.row;
I believe indexPath.row should return the index of the name that was tapped in the Table View Controller.
#import "ContactsViewController.h"
#import "Contacts-Swift.h"
#interface ContactsViewController ()
#property (nonatomic, readwrite, strong) NSMutableArray* contacts;
#property (nonatomic, readwrite) NSInteger myIndex;
#end
#implementation ContactsViewController
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
/*NSArray *contactArray = #[#"Johnny Appleseed", #"Paul Bunyan", #"Calamity Jane"];
_contacts = [NSMutableArray arrayWithArray:contactArray];*/
/*Contact *c1 = [[Contact alloc] initWithName: #"Johnny"];
Contact *c2 = [[Contact alloc] initWithName: #"Paul Bunyan"];
Contact *c3 = [[Contact alloc] initWithName: #"Calamity Jane"];*/
// _contacts = [NSMutableArray arrayWithArray: #[c1, c2, c3]];
self.contacts = [NSMutableArray array];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.tableView registerClass: [UITableViewCell class]
forCellReuseIdentifier:#"UITableViewCell"];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return self.contacts.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell" forIndexPath:indexPath];
Contact *contact = self.contacts[indexPath.row];
cell.textLabel.text = contact.name;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ContactsViewController *viewController = [self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"the"];
[self.navigationController pushViewController:viewController animated:YES];
_myIndex = indexPath.row;
}
File: ContactsViewController.h
This is the header file that I am using in order to have access to the objective C methods and variables when working in the swift file. (I am not very familiar with objective C so there is a strong possibility that this implementation is what is causing my problems).
#import <UIKit/UIKit.h>
#interface ContactsViewController : UITableViewController <UITableViewDelegate>
#property (nonatomic, readonly) NSInteger myIndex;
#property (nonatomic, readonly, strong) NSMutableArray* contacts;
#end
File: ExistingContactViewController.swift
In the ExistingContactViewController I am just trying to set the firstName label equal to the text that is present in the contacts array at indexPath.row (in the ContactsViewController.m file).
import UIKit
#objc class ExistingContactViewController: UIViewController {
var contactsObject = ContactsViewController()
#IBOutlet weak var firstName: UILabel!
#IBOutlet weak var lastName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("\(contactsObject.myIndex)")
firstName.text = contactsObject.contacts[contactsObject.myIndex] as? String
}
When clicking names that are added to the Table View Controller the only index that is ever printed
print("\(contactsObject.myIndex)")
is 0. Which tells me that I am not capturing the index of the name that is tapped.
Image of myStoryboard The bottom most scene is the one that I am trying to change the First Name label to display the name of the cell that was tapped.
I have not yet been able change the title of this label when clicking on a cell. I have been able to implement this functionality when using just swift files (through watching numerous videos). I am sure that there is a key concept I am missing in the objective C files so any suggestions and/or pointers are much appreciated. If any additional details are needed let me know!
Thanks.
Seems that in your didSelectRowAtIndexPath: you are pushing a ContactsViewController to the navigation stack and if I understand correctly it should be instance of ExistingContactViewController. Also you should set the contactsObject property of the ExistingContactViewController before pushing it in the navigation stack (before viewDidLoad is executed) otherwise it's value will always be a new ContactsViewController which, probably, is causing the issue.
I hope this helps!

segue to a view from a tableview in different class

To explain the issue, I had ViewControllerA with a UITableView called commentTableView inside of it. and from commentTableView i would segue to ViewControllerB but I needed to add an addition UITableView called mentionedFriendTable to ViewControllerA. But once i did add mentionFriendTable I started to have issues with the tables that caused the app to crash.
Issue Im Having
I decided to place commentTableView into a different class called justCommentsTable which is a UITableViewController class and add that class to ViewController. I have to place commentTableView in a different class and not mentionFriendTable because mentionFriendTable needs to be in ViewControllerA. and after a couple of hours I finally got that to work. but now that segue I initially had to ViewControllerB does not work, and crashes saying
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<justCommentsTable: 0x21032160>) has no segue with identifier 'segueToViewControllerB'
My Storyboard
On my storyboard i have ViewControllerA with a tableView inside of it and i linked up the tableview to #property (strong, nonatomic) IBOutlet UITableView *commentTable;
in the code below I explain how commentTable becomes the tableview from justCommentsTable
I know I'm getting this crash because ViewControllerB is connected to ViewControllerA through MainStoryboard.storyboard and not justCommentsTable
My Question
Is there a way to still segue ViewControllerA to ViewControllerB and pass data from commentTableView which is in a different class.
I'm gonna go ahead and place whatever code I find relevant to the issue. Just tell me if im missing any crucial code.
ViewControllerA.h
#import "justCommentsTable.h"
#interface ViewControllerA : UIViewController<UITableViewDelegate, UITableViewDataSource>{
// in justCommentsTable.m I add this controller to the view
justCommentsTable *commentsController;
}
// this is the table that is link up in storyboard
#property (strong, nonatomic) IBOutlet UITableView *commentTable;
//this is my mentions table that needs to be in ViewControllerA
#property (nonatomic, strong) UITableView *mentionTableView;
#end
ViewControllerA.m
- (void)viewDidLoad
{
[super viewDidLoad];
**// i set up the mentionTable here**
self.mentionTableView.transform = transform;
self.mentionTableView.delegate = self;
self.mentionTableView.dataSource = self;
self.mentionTableView.tag = 1;
[self.view addSubview:self.mentionTableView];
**// i set up the commentTable here**
if (commentsController == nil) {
commentsController = [[justCommentsTable alloc] init];
}
[commentTable setDataSource:commentsController];
[commentTable setDelegate:commentsController];
[commentsController setHomeUserID:homeUserID];
[commentsController setGetEventHostIDforNotif:getEventHostIDforNotif];
[commentsController setGeteventIDfrSEgue:geteventIDfrSEgue];
[commentsController setGetEVENTNamefrSegue:getEVENTNamefrSegue];
**// i set commentsController to the Tableview in justCommentsTable class**
commentsController.view = commentsController.tableView;
}
justCommentsTable.h
#interface justCommentsTable : UITableViewController<UITableViewDelegate, UITableViewDataSource,UITextFieldDelegate,UITextViewDelegate>
#end
justCommentsTable.m
#implementation justCommentsTable
//buttonTag is used for a button on the customCells
int buttonTag;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"commentCell";
customCell *cell =(customCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *commmentDict = [CommentArray objectAtIndex:indexPath.row];
NSString *commentText = [commmentDict objectForKey:#"comment"];
cell.textLabel.text = commentText;
// i need a custom button on the cells for other reasons
[cell.cellButton addTarget:self action:#selector(showButtonIndex:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
-(void)showButtonIndex:(UIButton*)button{
buttonTag = button.tag;
/*
buttonTag gets set here, and used in the prepareForSegue method
to find out what row the button was on.
*/
[self performSegueWithIdentifier:#"segueToViewControllerB" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"segueToViewControllerB"]){
//segue to profile from image button
ViewControllerB *VCB = segue.destinationViewController;
NSDictionary *commentDic = [CommentArray objectAtIndex:buttonTag];
/*
buttonTag was used to select the objectAtIndex
*/
NSString *commentTitle = [commentDic objectForKey:#"comment_title"];
VCB.title = commentTitle;
}
}
I know where my issue is, I just have no idea on how to fix it. and Ive searched for problems like mine, but I cant find one with a solid answer. if anyone can help, it would greatly be appreciated. thanks!

Creating a Custom Table Cell, Cannot Access Property

IMPORTANT EDIT: I posted the wrong error code, like an idiot. I was posting the error for an attempt I had previously made to fix the issue, instead of the first error. Disregard my dumbness, please.
I'm creating a Facebook Feed app in Xcode, and I'm running into trouble in the creation of custom cells for a table. I'm trying to assign values to two UILabels on the custom cell, and it's giving me the error "No visible #interface for 'JSONFeedItemCell' declares the selector 'nameLabel'". My code is as follows:
Master View Controller
- (void)viewDidLoad
{
UINib *nib = [UINib nibWithNibName:#"JSONFeedItemCell" bundle:nil];
[[self tableView] registerNib:nib forCellReuseIdentifier:#"JSONFeedItemCell"];
... // other stuff, not relevant
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JSONFeedItemCell *cell = [tableView dequeueReusableCellWithIdentifier:
#"JSONFeedItemCell"];
NSDictionary *p = [[[JSONFeedItemStore sharedStore] allItems]
objectAtIndex:[indexPath row]];
[[cell nameLabel] setText:#"The Name"];
return cell;
}
Cell Class
#import <Foundation/Foundation.h>
#interface JSONFeedItemCell : UITableViewCell
{
}
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#property (weak, nonatomic) IBOutlet UILabel *detailLabel;
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#end
Let me know if you need any additional information or code, I'd be happy to provide it.
Two things: you have to make sure.
#import "JSONFeedItemCell.h" //in your mainViewController.h
And, as Wolfgang Schreurs suggested, typecast the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JSONFeedItemCell *cell = (JSONFeedItemCell *)[tableView dequeueReusableCellWithIdentifier:
#"JSONFeedItemCell"];
NSDictionary *p = [[[JSONFeedItemStore sharedStore] allItems]
objectAtIndex:[indexPath row]];
[[cell nameLabel] setText:#"The Name"];
return cell;
}
EDIT: since you don't use custom setters/getters you have to synthesize the properties
in JSONFeedItemCell.m
#synthesize imageView;
#synthesize detailLabel;
#synthesize nameLabel;
Compiler should warn you if you forgot to do that but with all the possible compiler settings you never know.
Do you maybe have something like a , I call it, circle import? Xcode gets messed up when you have 2 classes which imports each other. Xcode displays sometimes 'random' errors like this. And sometimes helps to clean and organize project, and restart pc. I have actually no idea why, but it helps sometimes.

NSMutableArray Returns NULL

I try adding objects to NSMutableArray from another class (secondViewController) and then add it to my UITableView in my FirstViewController, but it returns null when I print it using NSLog. Here is my set up.
FirstViewController.h:
#interface FirstViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>{
IBOutlet UITableView *mytableview;
NSMutableArray *mytableinfo;
}
#property (nonatomic,retain) IBOutlet UITableView *mytableview;
#property (retain) IBOutlet NSMutableArray *mytableinfo;
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#synthesize mytableinfo,mytableview;
-(IBAction)addShift:(id)sender{
SecondViewController *secondViewController = [[SecondViewController alloc]init];
[self presentModalViewController:secondViewController animated:YES];
}
- (void)viewDidLoad
{
mytableinfo = [[NSMutableArray alloc]init];
[super viewDidLoad];
}
SecondViewController.m
#import "SecondViewController.h"
#import "FirstViewController.h"
#implementation SecondViewController
#synthesize dateformatter,mydatepicker,startingTime;
-(IBAction)saveShift:(id)sender{
FirstViewController *firstViewController = [[FirstViewController alloc]init];
[firstViewController.mytableinfo addObject:#"Hello world"];
NSLog(#"%#",firstViewController.mytableinfo);
[self dismissModalViewControllerAnimated:YES];
}
My goal is to ultimately feed a mytableviewfrom mytableinfo. I'm not even sure if this is the best way to go about it. Any advice would be appreciated.
In SecondViewController, you are creating a FirstViewController with alloc init. At that point, mytableinfo on FirstViewController is nil because you don't allocate until viewDidLoad.
What loads SecondViewController? Because you're dismissing it modally. If it's FirstViewController, then when you alloc init first view controller, you're not calling the instance that presented it modally.
It's also not very MVC to have one view poke at another like that. It creates code that's couple at the view layer and modifying data at the view layer. It is better to create a model and have both views modifying that model.
How to create a NSMutable Array which can access from different view controllers
Another way to communicate between views is for one view to pass a delegate (a callback) to the other view. That allows the other view to not be coupled to the other view - it only knows about the protocol for the delegate.
What exactly does delegate do in xcode ios project?
There is a point that look strange to me in your "SecondViewController" you dissmiss it like it's a modal.
My Question is then... who started the modal presentation?
A "FirstViewController"? If it's the case, why are you creating a new one, on dismissing the second, the First that launched it will resume it's activity.
An other thing that I don't understand is that the designated initializer for a UIViewController is
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
You can pass nil to both argument, if no nib need to be associated with.
And finaly, if you need to get back a NSMutableArray to a 1st ViewController (VC) from a 2nd VC that was modally presented by the 1st you can do this in the 2nd VC:
- (id)initWithMutableArray:(NSMutableArray *)theArray {
//... put standard init code here }
And make that the default initializer of your second VC. But this make sense only if 2nd VC absolutely need a mutable array.
And now for my curiosity because I don't understand this line
#property (retain) IBOutlet NSMutableArray *mytableinfo;
Why is this an IBOutlet? That look like a potential source of problem.
IBOutlet are usually pointers to UI elements in a xib file.
When populating a UITableView with an array that can be modified by multiple modal views during the course of your app, I find one of the best ways to do this is with NSUserDefaults. You can create an NSUserDefaults object for reference like this:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
Then you can assign objects to each key in defaults, which is really just a plist (which is just a list of keys with objects associated with them.
So then, when you want to store the array in defaults, you can say:
[defaults setObject:mytableinfo forKey:#"tableInformationKey"];
Then, whenever you want to access that data, you can say:
NSMutableArray* tableInfoCopy = [defaults mutableArrayValueForKey:#"tableInformationKey"];
That will make you a copy of the array you have stored in NSUserDefaults (NSUserDefaults can be accessed from anywhere in your app), so then you can make changes to that mutable array you just made. Once you are done making changes, you can reassign it to NSUserDefaults like this:
[defaults setObject:tableInfoCopy forKey#"tableInformationKey"];
So when you populate your UITableView, in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
put something like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Foobar"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Foobar"] autorelease];
// Initialize cell with some customization
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.textLabel.textColor = [UIColor blackColor];
}
NSArray* arrayOne = [defaults objectForKey:#"tableInformationKey"];
NSString* title = [arrayTwo objectAtIndex:indexPath.row];
//this goes to the index in the array of whatever cell you are
// at, which will populate your table view with the contents of this array (assuming the array contains strings)
// Customize cell
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:25];
return cell;
}
Use them the easiest way is to put the array in your AppDelegate
// populate appDelegate's array
AppDelegate *appDelegate = (myAppDelegate *) [[UIApplication sharedApplication] delegate];
[appDelegate.arrayMyTableInfo addObject:#"HelloWorld"];
Here is how you can do what I've suggested :
(This is part of your code updated)
This is in your secondView, this is one of many way to pass the array to your second view.
#synthesize array4Test;
- (id)initWithMutableArray:aMutableArray
{
self = [self initWithNibName:nil bundle:nil];
if (self)
{
self.array4Test = aMutableArray;
}
return self;
}
- (void)dealloc
{
// HERE clean up the property is set to retain.
self.array4Test = nil;
[super dealloc];
}
Here is the code for the firstView
-(IBAction)addShift:(id)sender{
SecondViewController *secondViewController = [[SecondViewController alloc] initWithMutableArray:self.mytableinfo];
[self presentModalViewController:secondViewController animated:YES];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.mytableview reloadData];
NSString *aString = [mytableinfo lastObject];
if (aString)
{
NSLog(#"This just came back from the second View\n%#", aString);
}
}

How to reload UItableView that is inside UiViewController

I have a UITableView inside UiViewController.
In the same screen, I also have two buttons: Show All vs Show Top 5.
Now based on a selection all/top 5, I have to update table data.
I cant use [tableView reloadData] as I am not using UITableViewController.
This is the first time I am working on an iphone app. So any help is appreciated.
(I used this tutorial to get started http://www.icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/)
Thanks.
Here is a snippet of my code:
.h file
#interface DataViewController : UIViewController {
NSString *showType;
}
#property (nonatomic, retain) NSString *showType;
-(IBAction) showTop: (id) sender;
-(IBAction) showAll: (id) sender;
#end
.m file
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"customCell";
DataCustomCell *cell = (DataCustomCell *) [tableView dequeueReusableCellWithIdentifier:cellID];
if([showType isEqualToString:#"all"])
{
// use this data..
}
else
{
// use some other data..
}
// ....
}
-(IBAction) showNearby: (id) sender
{
self.showType=#"top";
// reload table some way
}
-(IBAction) showAll: (id) sender
{
self.showType=#"all";
//reload table some way
}
Create a UITableView IBOutlet like this
#property (nonatomic, retain) IBOutlet UITableView *myTableView;
in your UIViewController's interface file. Then have that connect to the UITableView in Interface Builder. After synthesizing it in your implementation file, you should be able to access it like this
[self.myTableView reloadData];
Also, since you retained it, you will have to release myTableView in the dealloc method.