Accessing array from a different view controller issue - iphone

I'm trying to access an array from a custom UITableViewCell subclass. The array is created in my tableViewController. This is driving me crazy. I access other view controller's objects all the time with ViewController *vcInstance. I actually need to edit the array from my cell subclass, but I can't even NSLog it from my cell view controller. The array logs perfectly in my tableViewController. All I get is null from the cell subclass.
CustomCell.h
#property (retain, nonatomic) SongsViewController *vc;
CustomCell.m
#synthesize vc;
-(IBAction)setStateOfObjects
{
NSMutableArray *array = [[NSMutableArray alloc] initWithArray:vc.parseTrackArray];
NSLog(#"%#", array);
}
I've also simply tried: CustomCell.m
-(IBAction)setStateOfObjects
{
SongsViewController *vc;
NSLog(#"%#", vc.parseTrackArray);
}

EDIT: You're not quite understanding how object referencing works. When you request an object from an array, or some other object that is "retaining" it, you are not creating a new object. So, you don't end up with your "previous object" and an "updated object". Consider this:
NSMutableDictionary *dict = [array objectAtIndex:index];
[dict setObject:#"Hello" forKey:#"Status"];
//You don't need to add *dict back to the array in place of the "old" one
//because you have been only modifying one object in the first place
[array replaceObjectAtIndex:index withObject:dict]; //this doesn't do anything
Considering that...
The way you're going about this is backwards. Make a property in your UITableVieCell subclass for your array
interface CustomCell : UITableViewCell
#property (nonatomic,retain) NSMutableArray *vcArray;
#end
#import CustomCell.h
#implementation CustomCell
#synthesize vcArray;
-(IBAction)setStateOfObjects {
NSMutableDictionary *dictionary = [parseTrackArrayToBeModified objectAtIndex:currentIndex];
[dictionary setObject:[NSNumber numberWithBool:YES] forKey:#"sliderEnabled"];
//THIS LINE IS REDUNDANT
//[parseTrackArrayToBeModified replaceObjectAtIndex:currentIndex withObject:dictionary];
//END REDUNDANT LINE
}
//in your ViewController's delegate method to create the cell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//assume previous code has CustomCell created and stored in variable
CustomCell *cell;
cell.vcArray = self.parseTrackArray;
return cell;
}

Retaining your SongsViewController seems like a bad idea. If you are using iOS 5, that should probably be weak, if before iOS 5, assign. You're likely creating a retain cycle (memory leak) with that.
When you create the CustomCell (probably in tableView:cellForRowAtIndexPath:) in SongsViewController, are you setting it's vc property?
[yourCell setVc:self];

Related

Add Objects From One View Controller To An Array On Another View Controller

I have been trying to figure this out for a while and not coming up with a solution. I have a view controller with a table and the first cell of the table is allocated for a button called "Add Friends". When clicked, it takes you to another view controller with a list of contacts in a table. When you click on a person, it goes back to the other view controller and adds the selected person. This is what I have so far.
ContactsViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FirstViewController *newVC = [self.storyboard instantiateViewControllerWithIdentifier:#"newVCSegue"];
newVC.peopleArray = [[NSMutableArray alloc] init];
Person *user = [contactsList objectAtIndex:indexPath.row];
NSArray *userKeys = [NSArray arrayWithObjects:#"FirstName", #"LastName", nil];
NSArray *userObjects = [NSArray arrayWithObjects:user.firstName, user.lastName, nil];
NSDictionary *userDictionary = [NSDictionary dictionaryWithObjects:userObjects forKeys:userKeys];
[newVC.peopleArray addObject:userDictionary];
[self.navigationController pushViewController:newVC animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
FirstViewController.h
#property (strong, nonatomic) NSMutableArray *peopleArray;
FirstViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
if (indexPath.row == 0) {
contactName.text = #"Add Person";
imgView.image = [UIImage imageNamed:#"plus-icon.png"];
} else {
NSString *firstName = [[peopleArray objectAtIndex:(indexPath.row)-1] objectForKey:#"firstName"];
NSString *lastName = [[peopleArray objectAtIndex:(indexPath.row)-1] objectForKey:#"lastName"];
contactName.text = [NSString stringWithFormat:#"%# %#", firstName, lastName];
}
return cell;
}
This lets me add one friend so far and if I decided to add another to the list, it replaces the first friend added.
What's basically happening is every time you select a new contact, you're recreating the array in the first view controller, hence it is replacing things. You ideally want to try and avoid getting the FirstViewController using the storyboard like that as well, it's pretty bad practice and may well lead to various problems later.
What I'd suggest in this situation is creating a protocol (look at the delegate pattern). This way, what you'd have is :
Use taps "Add Contact"
Contacts list appears, and FirstViewController is set as the delegate
User taps contact to add them
ContactsViewController informs the delegate of the user that was selected
FirstViewController adds the user, and dismissed the view controller
This is generally the approach you'd take, and it's pretty simple to implement. Start with the protocol
#protocol ContactsDelegate
-(void) contactsViewController:(ContactsViewController *)vc didSelectContact:(Person *)person;
#end
Then, make your FirstViewController implement this protocol. To do this, in your header file, in the angle brackets after the name (< >) add ContactsDelegate
In the implementation of FirstViewController, add the new method of the contacts delegate.
In your ContactsViewController.h file, add
#property (nonatomic, assign) NSObject<ContactsDelegate> *delegate;
Then when you display your contacts view controller, set the delegate
userVc.delegate = self;
[self presentModalViewController:userVc];
Then, in the user view controllers didSelectRowAtIndexPath:, simply inform the delegate that you've selected that person
[delegate contactsViewController:self didSelectContact:[contactsList objectAtIndex:indexPath.row]];
And lastly, in your FirstViewController, in the delegate method you added, we need to ADD the user to the list, not re-create the list
[peopleArray addObject:person];
And that should do what you're after :)
From what I understand, you are instantiating a new FirstViewController every time you select a contact in the ContactsViewController. Instead, you should reference the original FirstViewController (perhaps save it before transitioning to ContactsViewController), and use this reference to add the contact to the original array [original.people addObject:userDict]. As long as you make sure to reload the table, this should work.

nsmutablearray to tableview

I have a nsmutablearray which im trying to display on a table view. i have a uiviewcontroller and added the table view manually. i have added the delegate and source on the xib by draggin it to the file owner. here is my .h file
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
{
IBOutlet UILabel *label;
IBOutlet UITextField *texto;
IBOutlet UIWebView *link;
//IBOutlet UILabel *links;
IBOutlet UITextView *links;
// IBOutlet UITableView *tableView;
NSMutableArray *jsonArray;
}
#property (nonatomic,retain) IBOutlet UITableView *tableView;
//#property (nonatomic,retain) NSMutableArray *jsonArray;
-(IBAction)button;
-(IBAction)field;
-(void)populateArray;
#end
and here is my .m file
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
NSString *one;
NSString *jsonreturn;
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"www.myphpfile.com"];
jsonreturn = [[NSString alloc] initWithContentsOfURL:url]; // Pulls the URL
NSLog(jsonreturn); // Look at the console and you can see what the restults are
NSData *jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSError *error = nil;
jsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:&error];
NSLog(#"jsonList: %#", jsonArray);
if(!jsonArray)
{
NSLog(#"Error parsing JSON:%#", error);
}
else
{
// Exception thrown here.
for(NSDictionary *item in jsonArray)
{
NSLog(#"%#", item);
}
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [jsonArray 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];
}
NSLog(#"Im HERE!!!");
cell.textLabel.text = [jsonArray objectAtIndex:[indexPath row]];
NSLog(#"Cell is %#", [jsonArray objectAtIndex:indexPath.row]);
return cell;
}
my question is, i cant get the cells to show the date, i have the ns logs and it shows that it is pulling it, also im getting a thread 1, breakpoint 1.1 error where the the cell is created and a warning on the same line sayin tableView is local and hiding instance variable, any thoughts? thanks for the help!
update:
I got my program to compile but it throws an exception:
2013-03-26 22:28:48.691 Hello[79888:11303] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key tableView.'
does this have to do with the information im givin the table or im creating the table wrong? thanks again!
If you've added the tableview through the interface builder/storyboard try adding:
#synthesize tableView;
under your #implementation in your .m file. Then make sure you have the tableview connected properly in your IB. (To the View Controller as both data source and delegate and then form the VC to tableview as tableView.)
NSString *one;
NSString *jsonreturn;
The two statements lack standardization.
If you have not using ARC, jsonreturn will leak.
You are getting the warning because you have an instance variable called tableView and also inside the table view datasource methods tableView is passed in as a parameter. Changing this variable name or the instance variable name would solve the problem. Since they both are referring to the same thing, it doesn't matter.
And if its just stopping at a point saying Stopping at Breakpoint 1 you probably have set a breakpoint. Its looks like a blue arrow mark , located on the left hand side of the code. You can disable it by clicking on it again(becomes a lighter shade) or by deleting it- drag the breakpoint out or right click on the breakpoint and delete.
look at the variable name of your tablview
IBOutlet UITableView *tableView
its same as in built method of UITableView uses
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
thats why its giving you warning, "tableView is local and hiding instance variable"
to avoid it just change your UITableView variablename to something "myTableView"
First of all allocate your NSMutableArray, Like this " ArrayName = [[NSMutableArray alloc] init];

How do I add a UITableView to a parent that contains other objects?

This is essentially the layout I want:
The UITableView at the bottom should accomodate comments to a specific post, adding a row for each comment.
The UITableView at the bottom is wired to commentTable; all other elements are wired accordingly as well.
When I build and run, no errors, but I only see one empty table cell below the post.
I know there's something missing in loading/passing data to my table, but I wonder if someone can give me a direction on how to make this work.
DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController {
IBOutlet UIImageView *postThumbView;
IBOutlet UILabel *postTextLabel;
IBOutlet UIImageView *postAuthorPictureView;
IBOutlet UILabel *postAuthorNameLabel;
IBOutlet UILabel *postTimestampLabel;
IBOutlet UIScrollView *scroller;
IBOutlet UITableView *commentTable;
}
#property (strong, nonatomic) id detailItem;
#end
DetailViewController.m
#import "DetailViewController.h"
#interface DetailViewController ()
- (void)configureView;
#end
#implementation DetailViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureView];
}
- (void)configureView
{
if (self.detailItem) {
NSDictionary *post = self.detailItem;
NSString *postText = [post objectForKey:#"post_text"];
...
postTextLabel.text = postText;
...
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *post = self.detailItem;
NSDictionary *commentThread = [post objectForKey:#"comment"];
return commentThread.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"commentCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *post = self.detailItem;
NSDictionary *commentThread = [post objectForKey:#"comment"];
NSString *commentText = [commentThread objectForKey:#"comment_text"];
NSString *commentAuthorName = [commentThread objectForKey:#"comment_author_name"];
cell.textLabel.text = commentText;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", commentAuthorName];
return cell;
}
#end
It may be that the table view delegate method's you've written aren't being called. The first thing you should do is set breakpoints inside these methods, run your app, and see if they are being called.
If they're not being called, you may have failed to set your delegate. In this case, it appears that you are not using a discrete UITableViewController, rather you are attempting to have your DetailViewController supply the necessary information for the tableView to work as expected.
First, you need to conform your DetailViewController to the UITableViewDelegate protocol:
#interface DetailViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
Second, you need to actually set the delegate #property of your UITableView. You can do this in interface builder (select the tableview, right click, drag it's delegate property to connect to your DetailViewController, which may or may not be File's Owner). If you'd rather do it in code, you just need to call (early in the VC's life, in viewDidLoad, for example):
self.tableView.delegate = self;
self.tableView.datasource = self;
So... assuming your delegate is all wired up properly, you should then go back and test those breakpoints to see if the table view's methods are being called. If they are being called, the next step would be to evaluate the variables when the breakpoints are called, examine for example if the numbers being return in numberOfRowsInSection and the values in cellForRowAtIndexPath match what you anticipate.
You need to declare your view controller as the delegate and data source for the table view
change this line in your .h file
#interface DetailViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
Then in your viewDidLoad
commentTable.dataSource = self;
commentTableView.delegate = self;
[commentTableView reloadData];
[self configureView];
You can also look in the story board, and connect the outlets n the same way you connected commentTable to your UITableView, but by dragging in the opposite direction and selecting data source and delegate

Passing an NSMutableArray to a DetailView from UITableViewController - iPhone

I have a UITableViewController which presents another UITableViewController when a cell is tapped. I have an NSMutableArray which I want to pass into the new UITableViewController when instantiated.
I would usually do something like :
- (void)loadStationList {
StationListView * listView = [[StationListView alloc] initWithNibName:#"StationListView" bundle:nil];
listView.dataList = newParser.stationData;
// ...
// Pass the selected object to the new view controller.
NSLog(#"New Parser is %d", [newParser.stationData count]); //This is fine - all objects in array here.
[self.navigationController pushViewController:listView animated:NO];
}
The odd thing is that dataList (the NSMutableArray pointer in the new class) is empty (I am in checking in the number of rows delegate method).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Data List is %d", [dataList count]);
return [dataList count];
}
I have tried several different approaches (such as instantiating a new NSMutable array in the parent class) however nothing seems to work. This may be ARC related as I am still relatively new to ARC. Can anyone help ?
How did you declare dataList,newParser.stationData ?
Should be sth like this
#property (nonatomic, strong) NSMutableArray *dataList;
Anyway, to ensure that you do not loose any values, you may want to copy/assign each element from newParser.stationData to dataList.
Like here:
NSMutableArray * dataList = [[NSMutableArray alloc] init];
for (id arrayElement in newParser.stationData) {
[dataList addObject:arrayElement];
}
Try this instead of the simple assignment listView.dataList = newParser.stationData;. ps don't worry about efficieny this is so fast it will not matter.
Well I got round this by created an instance variable in the app delegate and then saving and reading the array from there. This does look like a bug in arc - it works with other variable types.

UITableView refreshing problem

I am trying to refresh an UITableView every time I navigate the the view that contains this Table.
I Have a ViewController and a custom UITableViewController that manages to set the Table Correctly when the application starts, using an NSMutableArray contained inside the controller.
When I navigate to the page containing the table, the ViewController calls a function that gets the data from a server with an HTTP request and parse it in an NSMutableArray.
Now here is my problem. I manage to send this array to my UITableViewController, but when I want to refresh my tableView, nothing happens.
I tried to use [myTable reloadData], but it doesn't calls the numberOfRowsInSection, or cellForRowAtIndexPath functions. I saw that people with the same problem solved it using [self.myTable ReloadData], but I get an error :
accessing unknown getter/setter method
I am pretty new to objective-C, and this error is still a bit mysterious to me as I get it a bit randomly.
Anyway, there is a high probability that I made a mess with the declaration of the UITableViewController (where am I supposed to declare it?) and with the Interface Builder links, so this can be a clue to find the solution.
Any one have an idea?
Thank you very much!
EDIT : Here is my tableview controller class:
#import "MyCell.h"
#class Mycell;
#interface MyTableController : UITableViewController {
IBOutlet MyCell * myCell;
IBOutlet UITableView * myTable;
NSMutableArray *data;
}
#property (nonatomic, retain) IBOutlet UITableView * myTable;
- (void) EditTable : (NSMutableArray*) param;
#end
And now the .m:
#implementation MyTableController
#synthesize myTable;
- (void) viewDidLoad {
[super viewDidLoad];
myTable = [[UITableView alloc] init];
data = [[NSMutableArray alloc] init];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyCell";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; >
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (MyCell *) currentObject;
}
}
}
NSString *datastring = [listenom objectAtIndex:indexPath.row];
[cell setCell: datastring ];
return cell;
}
- (void) EditTable : (NSMutableArray*) param{
//This function is called by the ViewController when the user goes to the page containing the view
data = param; //The param array contains the data from the HTTP request
[self.tableView reloadData];
[self.myTable reloadData]; //I tried both, but only the first one actually calls the previous functions
}
You have a number of problems in this code sample. I'll point out a few of them here but I highly recommend reading the relevant Apple documentation at:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
and
http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/TableView_iPhone/AboutTableViewsiPhone/AboutTableViewsiPhone.html
Some issues in your code:
Since the class MyTableController is a subclass of UITableViewController you don't need the attribute and property for myTableView. The tableView property is defined and initialized as part of UITableViewController's implementation with its dataSource and delegate set to the UITableViewController instance. This is why [self.tableView reloadData] is calling your delegate and dataSource protocol methods.
You are also using interface builder so if you did want to create your own subviews you should either do that within IB and set the outlet there or do it in your code which means creating the subview(s) in viewDidLoad and then adding them to your view with [view addSubview:mySubView].
A better way to set the data for your table would be to create a property for your data attribute and call setData from the view controller that has initialized the MyTableController instance. You would use the setData: method to do this. You can call [self.tableView reloadData] in setData. You don't need to explicitly reload the table when the view is loaded as this is done automatically. A more minor point, if you stay with EditTable I would rename it to be more descriptive and to use camel case (e.g. setDataForTable`) to be consistent with iOS conventions.
You don't show any init/alloc for the listenom attribute referenced in tableView:cellForRowAtIndexPath:. Did you mean to use data instead?
Is your MyTableController.m file the complete version? If so, you are missing viewDidUnload and dealloc methods. Both of which are required. viewDidUnload should release any objects allocated in viewDidLoad and dealloc should release anything retained by the controller (including objects released in viewDidUnload.
As you are using tableViewController you should be able to use self.tableView instead to reload the data like this
[self.tableView reloadData];
you need to synthesize first then you can use self.myTable
do on the top
#synthesize myTable
and then
[self.myTable reloadData];