iPhone add to NSMutableArray from another viewController - iphone

I have a bit of a dilemma and I was wondering if the good folk here could lend me their programming expertise. I'll try to be as simple and precise as I possibly can so here goes:
I'm a new IOS developer and have only been learning for a couple of months. I am developing an iPhone application for my dissertation at university. The app is simply a guide for people who wish to develop for the iPhone themselves, consisting of tutorials. It consists of numerous Table Views but there is one thing that has got me stumped.
What im trying to do:
One feature im trying to include in my app is a bookmarks facility, this will be accessible from a tab bar. I want to be able to click on a button from any nib file (tutorial) which adds a string to an existing NSMutableArray. This string will correspond with the name of the tutorial where the IB-Action was performed and after added to the Array I can load the nib file when selecting the row at index path.
The Problem:
I can add any object to the array from within the implementation file that contains the array but cannot figure out how to add it from a different implementation file. The UITable view populates from the array perfectly but adding a new entry is another story.
I'll show you my code but i'll leave out anything that is unrelated.
BookmarksViewController.h
#interface BookmarksViewController : UITableViewController {
NSMutableArray *bookmarksArray;
}
#property (nonatomic, retain) NSMutableArray *bookmarksArray;
#end
BookmarksViewController.m
-(void)viewDidLoad {
bookmarksArray = [[NSMutableArray alloc] init];
NSLog(#"String Added");
[bookmarksArray addObject:#"String"];
[super viewDidLoad];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Configure the cell...
cell.textLabel.text = [bookmarksArray objectAtIndex:indexPath.row];
return cell;
}
-(void)dealloc {
[bookmarksArray release];
[super dealloc];
}
NOW FOR THE SECOND VIEW CONTROLLER
Ch01GettingStarted.h
#interface Ch01GettingStarted : UIViewController {
IBOutlet UIScrollView *ScrollView;
}
-(IBAction) pushChap01Bookmark:(id)sender;
#end
Ch01GettingStarted.m
-(IBAction) pushChap01Bookmark:(id)sender{
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
[bookmarksViewController.bookmarksArray addObject:#"NewString"];
NSLog(#"ADD ENTRY");
[bookmarksViewController.tableView reloadData];
NSLog(#"RELOAD TABLE");
[bookmarksViewController release];
NSLog(#"ADD BOOKMARK RELEASE");
}
BTW - the IB-Action was declared in the header file.
Ah, i originally tried doing this as '[BookmarksViewController.bookmarksArray addObject:#"NewString"];' but I came up with an "expected ':' at '.'" error and I read somewhere that I needed to use an instance variable of BookmarksViewController so i declared it just above the addObject method.
Please be gentle with me as I haven't been doing this for long but this is certainly something that's going to be a big part of my professional future.
Any insight anyone could offer to me would be magical.
Thank you, thank you, thank you.

The problem lies in this method:
-(IBAction) pushChap01Bookmark:(id)sender
You are creating a NEW bookmarksViewController here, by doing this:
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
You don't want to do that because you want to update the current view controller. You need to create a link from Ch01GettingStarted.
Assuming you are using Interface Builder, you could create this link using an IBOutlet. In the Ch01GettingStarted interface, add the following line:
IBOutlet BookmarksViewController *bookmarksViewController;
(Between the brackets)
I think you already know how to link this in Interface Builder.
Then just remove this line:
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
And this line:
[bookmarksViewController release];
And it should work.
Why? The 'bookmarksViewController' variable now references the original object (the one you created in Interface Builder) that is actually displayed.

Just to be sure.
Do you have this method in BookmarksViewController.m?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[bookmarksArray count];
}

Because after bookmarksViewController is created. ViewDidLoad method not run yet. bookmarksArray has not been created. So bookmarksArray = nil. You can't add object to nil object. In Second Viewcontroller, You should create bookmarksViewController in loadView method. And addobject in pushChap01Bookmark.
I'm not good English. I hope you can understand it. :D

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.

UITableView Passing Data

Sorry this is probably a newbie question to alot of you but I've been going round in circles for the last few hours.
I have a table and when the row is pressed pops to second view.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
BlogDetailViewController *blogDetailViewController = [[BlogDetailViewController alloc] initWithNibName:#"BlogDetailViewController" bundle:nil];
blogDetailViewController.title = [idArray objectAtIndex:indexPath.row];
blogDetailViewController.newsArticle = [content objectAtIndex:indexPath.row];
[self.navigationController pushViewController:blogDetailViewController animated:YES];
}
Then on my next view I have a text box which i want to display the contents of [content objectAtIndex:indexPath.row];.
So far i have added this into the secoundview.h
#interface BlogDetailViewController : UIViewController{
NSDictionary *newArticle;
IBOutlet UITextView *descTextView;
}
#property (nonatomic, copy) NSDictionary *newsArticle;
I have also #synthesize newsArticle; on the secoundview.m, and linked up the IBOutlet.
So far i have
- (void)viewDidLoad
{
[super viewDidLoad];
descTextView.text = [content];
// Do any additional setup after loading the view from its nib.
}
Hope that explains things I can upload the zip if this does not make sense.
Thanks
I'm a bit puzzled with the question as it's not super clear, but I think what you're doing is passing data from the table view to another view, but the next view isn't displaying the data? The simplest thing to do in that case is add into viewWillAppear code that puts the content (newArticle?) into the descTextView.
descTextView.text = [NSString stringWithFormat:newsArticle]; this displayed the string inside the textview

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.

UIPickerView appears but doesn't show data

I've been all over the place for the last two days trying to find the answer to this, but I can't seem to get it. As far as I can tell, I have everything setup correctly, but it just doesn't want to work.
My app scans QRCode tags and saves them to a mutable array. I need to create a way so the user can remove some of the tag objects from the array, so I'm using a UIPickerView to display the list of tags numbers from which to select the tags to delete.
Here's what I have:
in the .h file
#interface ViewController : UIViewController
UIPickerView *tagPickerView;
NSMutableArray *tagPickerData;
#property (nonatomic, retain) IBOutlet UIPickerView *tagPickerView;
#property (nonatomic, retain) NSMutableArray *tagPickerData;
in the .m file
#synthesize tagPickerView;
#synthesize tagPickerData;
-(void) viewDidLoad{
tagPickerData = [[NSMutableArray alloc]init];
[tagPickerView setDelegate:self];
[tagPickerView setDataSource:self];
}
- (void) dealloc{
[tagPickerView release];
[tagPickerData release];
[super dealloc];
}
- (void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
{
[tagPickerData addObject:tagString]; //tagString is the value returned from the QRCode reader
}
#pragma mark -
#pragma mark tagPickerView Data Source Methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)tagPickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)tagPickerView
numberOfRowsInComponent:(NSInteger)component{
return [tagPickerData count];
}
#pragma mark tagPickerView Delegate Methods
-(NSString *)pickerView:(UIPickerView *)tagPickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [self.tagPickerData objectAtIndex:row];
}
(I think that's everything.)
In IB I have dataSource, delegate and tagPickerView set for File's Owner. The UIPickerView is hidden until the delete command is called (in this case, shaking the phone), and then it appears on top of everything.
What I am getting is a blank picker view.
I can verify using NSLog that the array is being populated each time the scan is performed, so I know it's not that I am loading an empty array. I can also verify that the Data Source methods are apparently being read, because I can change the number of components to 2 and it is reflected when the picker comes up -- two spinning wheels. But I don't know how to verify that the Delegate method is working.
I wondered if there's a way to populate the picker on command, like when the phone is shaken, instead of using the delegate... or would it even work that way?
The problem with all the reference materials is that it demonstrates how to use an array that is initialized with objects, i.e.
NSArray *array = [[NSArray alloc] initWithObjects:#"String 1",#"string 2",#string 3",(etc.) ,nil];
But I can't seem to find anything that shows how to load the picker from an array created from information collected by the user. Common sense tells me that it should work the same way, but I have learned to abandon common sense when working with Objective-C.
Is there a UIPickerView expert that can help me out here?
Thanks
Whenever you change the contents of the tagPickerData array, you need to call [tagPickerView reloadAllComponents]. The picker doesn’t have any knowledge of its underlying data—you have to send it that message to let it know that it should call its data-source methods.

The right way to hand over an object to a view?

i'm a bit new to iphone development. I made it to develop a nice App which loads XML Data from a feed, displays this data in a UITableView and if a user taps a row there should be a detail view which displays the data.
Thats where i got stuck a little bit. It's not clear for me how to hand over the data of the entry selected by the user to my detail view. The Detail-View is called via presentModalView...
I thought about:
Calling a "setDetails:(PostingData *)myPosting" function of the viewController of my detail view.
presenting the detailView to the user by calling presentModalViewAnimated.
The view is presented, but the setDetails: function crashes without any output to the debugger console.
MY QUESTION:
What is the right way to hand over Data (in custom objects as instance of a self written class) from my view Controller to a View Controller which should display detail data.
Any hint or help is appreciated. I can't pay you for your help, but i'm on my way becoming better and then helping others too :-).
Method 1: Pass it in custom init method
In your Header File declare a property
#property (nonatomic, retain) id myDataObject;
And in your implementation use a custom init like this
-(id)initCustom:(id)myObject;
if(self = [super init]) {
myDataObject = [myObject retain];
}
return self;
}
Method 2: Use a property
Use #property in your Header
and #synthesize in your .m Implementation File
[CustomUIViewController* newViewController = [[CustomUIViewControlleralloc] init];
newViewConroller.myDataObject = myObject;
[view addSubview:newViewController.view];
[newViewController release];
define the custom object in your class. #property(nonatomic, retain) MyClass * myClass;
load the feed into a NSMutableDictionary and provide that to your class
[YOUR_VIEW_CONTROLLER *yourViewController = [[YOUR_VIEW_CONTROLLER alloc] init];
yourViewController.PROPERTY_DEFINED_BEFORE = yourObject;
[view addSubview:yourViewController.view];
[yourViewController release];
cheers