Passing NSArray from DidSelectRow of a table to another UITableview - ios

I am trying to pass an array from one table ,and populate it in another table.The parent table is place upon a UINavigationController say "mainNavig".The child table is placed in another ViewController of name "SongsofAlbum".My didSelectRowAtIndexPath of parent table is as follows,
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
albumName = [eliminateDupe objectAtIndex:indexPath.row];
temp = [dictforalbum allKeysForObject:albumName ];
songs = [NSMutableArray arrayWithCapacity:[temp count]];
for (NSString *filename in temp) {
[songs addObject:[[filename lastPathComponent] stringByDeletingPathExtension]];
}
NSLog(#"songs are %#",songs);
songObj = [[SongsofAlbum alloc]initWithNibName:#"SongsofAlbum" bundle:nil];
[mainNavig pushViewController:songObj animated:YES];
songObj.albumname = albumName;
songObj.songArray = songs;
NSLog(#"the song object array is %# ",songObj.songArray)
}
The nslog of songObj.songArray returns the data in the above method .But the problem I face is ,when I call this songArray in the child view controller it returns NULL . I even property synthesized the arrays. Any suggestions?

But the problem I face is ,when I call this songArray in the child
view controller it returns NULL . I even property synthesized the
arrays.
In other ViewController you are creating a new instance of this class. The new instance will be NULL.
You do not need to create new instance infact you need to use this same object's value from there.
You can do this thing by: How to make button in child view to update information in its parent view?

You can take your songs array in AppDelegate and then instead assigning songObj.songArray = songs you can assign it in your SongsofAlbum class's viewDidLoad like
AppDelegate *app;
- (void)viewDidLoad
{
[super viewDidLoad];
app=(AppDelegate *)[[UIApplication sharedApplication]delegate];
songarray= [[NSMutableArray alloc]initWithArray:app.songs];
}
Hope this will work.

Related

Passing data with delegate but still nil why?

I made 2 viewcontrollers and implemented on tabbar controller. I passed some data from A vc to B vc with using delegate. When I checked the log it showed me correct value. But when I moved to B vc the value I passed was nil. (the value is for tableview.) Here is my code.
in A vc
-(void)passData {
NSMutableDictionary *infoDic = [[NSMutableDictionary alloc] init];
[infoDic setObject:url forKey:#"file_url"];
[downloadArr addObject:fileInfoDic];
Bvc getDataFromA:downloadArr];
[Bvc reloadTableView];
}
in B vc
-(void)getDataFromA:(NSMutableArray *) downloadArr{
self.downloadArr = [downloadArr mutableCopy];
NSLog(#"my download list%#", self.downloadArr); // This time was ok.
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSLog(#"Array status %#", self.downloadArr);//This time it showed me nil
[self.tableView reloadData];
}
To have the array printed inside viewWillAppear without nil
NSLog(#"Array status %#", self.downloadArr);//This time it showed me nil
you need to give it a value before you show bVC from aVC , whatever you use present/segue/push , also don't forget to declare it as strong , you need to do this
bvc = [[self.tabBarController viewControllers] objectAtIndex:1];
[bvc loadViewIfNeeded];
[bvc getDataFromA:downloadArr];
Please check the way you move to B-VC and whether the instance of (B-VC) you used in the A -VC is same to self in the B-VC “viewWillAppear” method.
It seems when u move to B-VC, A new instance has been created.
Or you can implement the 'setDownloadArr' method, log the value and show when the value become nil.

collectionView nil when [self.collectionView reloadData] is called, reloadData not called

I'm making an app that pulls images from both Flickr, and Imgur using their API.
After populating my model that stores the image URL and the title of the image, I want to reload the UI so that, the images populate the collectionview, but when its called, collectionview is nil.
This delegate method is called in the class responsible for fetching images using the APIs.
-(void)refreshUIOnMainThread
{
photosFromWeb = [libraryAPI getPhotos];
if([self.delegate respondsToSelector:#selector(reloadUIAfterImageDownload:)]) {
[self.delegate reloadUIAfterImageDownload:photosFromWeb];
}
}
The delegate method is defined in ViewController.m, the class where the UICollectionView delegate functions should be called upon a call to reloadData.
-(void)reloadUIAfterImageDownload:(NSArray*)photosFromWeb
{
allPhotos = photosFromWeb;
NSLog(#"reloadUIAfterDelegate: Number of Photos in Photo Model: %lu\n",
(unsigned long)[allPhotos count]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"about to reload collectionview...\n");
//collectionview is nil, so reloadData is not called???????
[self.collectionView reloadData];
});
}
Originally I thought the photo Model array was 0, meaning 0 cells would populate.
-(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
NSLog(#"numberofItemsInSection: Number of Photos in Photo Model: %lu\n",
(unsigned long)[allPhotos count]);
return ([allPhotos count]);
}
but that's not the case.
NSLog(#"reloadUIAfterDelegate: Number of Photos in Photo Model: %lu\n",
(unsigned long)[allPhotos count]);
returns a count of 128 indicating that Photo objects are there, and in the debugger I find the collectionview has a nil value within the delegate method definition. Why could this be?
Repo: https://github.com/danielv775/Flickr-Image-Gallery/tree/master/Photo%20Gallery
These functions are in FlickrClient.m and ViewController.m
Ah I see the issue now. It lies in this line of LibraryAPI.m:
flickrClient = [[FlickrClient alloc]init];
vc = [[ViewController alloc]init];
flickrClient.delegate = vc;
You're creating a new instance of your view controller, so none of the IBOutlets are set up on this new instance. Instead, you need to set your delegate from ViewController.m like so:
- (void)viewDidLoad {
[super viewDidLoad];
LibraryAPI *libraryAPI = [LibraryAPI sharedInstance];
libraryAPI.flickrClient.delegate = self;
}
This assumes you have a flickrClient property on your LibraryAPI. You could also add a delegate property on LibraryAPI if you wanted.
NOTE: You also want to change your delegate property on flickrClient like so:
#property (weak, nonatomic) id <FlickrClientDelegate> delegate;
Delegates should not maintain strong references.

How to push from a UITableViewController to a UIViewController using NSDictionary?

I have tried to push to a UIViewController using an Array:
self.cities=#[#"New York", #"Chicago", #"Los Angeles", #"Miami"];
and then in my didSelectRowAtIndexPath:
[self.navigationController pushViewController:NewYorkViewController animated:YES];
but now all the items in the array push to NewYorkViewController. I believe an NSDictionary would work
NSDictionary *cities=#[#{#"city":#"NewYork",#"viewController":NewYorkViewController}, #{#"city":#"Chicago",#"viewController":ChicagoViewController}, #{#"city":#"LosAngeles",#"viewController":LosAngelesViewController}, #{#"city":#"Miami",#"viewController":MiamiViewController}];
However I'm a noob and have never worked with NSDictionary. If you would be so kind to explain how i would go about doing this. If you know a way using array's that would work too.
ps.
I don't use storyboards and could you also explain what I would do in my cellForRowAtIndexPath: and numberOfRowsInSection:
Thanks
I am guessing you want to transfer your data from the UITableViewController to NewYorkViewController. The usual way to do this is having a property in your NewYorkViewController say an array or whatver you want.
#property (nonatomic,retain) NSMutableArray *itemsArray;
and in didSelectRowAtIndexPath while creating NewYorkViewController's object
NewYorkViewController *newyorkObject = [NewYorkViewController alloc]init];
newyorkObject.itemsArray = self.cities;
[self.navigationController pushViewController:newyorkObject animated:YES];
and in viewDidLoad of NewYorkViewController add this:
NSLog(#"items array: %#",self.itemsArray); //to check whether you got what you passed from the previous class.
I am using the Windows System now, so the following code may contain errors.
Use reflection to get the IOS Class
And then use this to alloc an object and operate it
You can use [Object class] or NSClassFromString ("Class Name") to get type of class
like this:
NSString * ClassName = # "yourClassName";
Class * tempClass = NSClassFromString (ClassName);
//if your class is subclass of UIViewController
UIViewController * tempObj = [[tempClass alloc] init];
//operating it
[self.navigationController pushViewController: tempObj animated:YES];
[tempObj release];
For your code you can change it like this:
//NSDictionary =#{#"key",#"value",....}
NSDictionary *citieone=#{#"NewYork",[NewYorkViewController Class]}
NSDictionary *citietwo = #{#"Chicago",NSClassFromString(#"ChicagoViewController")};
//other citys
NSArray* cities = #{citieone,citietwo,...};
//in UITableView
NSString* cityName = [[[citis objectForIndex:index] allKeys] objectAtIndex:0];
Class* controllerClass = [[[citis objectForIndex:index] allValues] objectAtIndex:0];
// operating it
UIViewController * controller= [[controllerClass alloc] init];
[self.navigationController pushViewController: controller animated:YES];
[controller release];
I did something similar with my app, but I was using NSArray instead of NSDictionary, but the logic is similar IMHO.
I created a NSArray with my objects, and a method to return their count and also the name at index. This is so that when I want to get a particular object from the array I can access it quicky. I have changed my code to accept the NSDictionary so that you can use.
In order to create the number of rows in the UITableView, I created a method that returns the number of rows when called.
-(NSInteger) numberOfCities
{
NSArray * allKeys = [mutableDictionary allKeys];
return [allKeys count];
}
This will be called in the
- (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section
{
return [self numberOfCities];
}
so that the UITableView will have the exact number of rows as the NSDictionary
In order to return the name at a particular index so that you can display that name in the UITableViewCell, I created this method
-(NSString*) cityNameAtIndex: (NSInteger) index
{
return NSString* cityName = [[[mutableDictionary objectForIndex:index] allKeys] objectAtIndex:index]; ;
}
This method can be called in the
- (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) indexPath
to assign the different city names to different cells in the table.
Then in the delegate
- (void) tableView: (UITableView*) tableView didSelectRowAtIndexPath: (NSIndexPath*) indexPath
you can call this method and push to the different viewControllers
UIViewController* selectedController = nil ;
NSString* nameOfCity = [self cityNameAtIndex: indexPath.row] ;
if( [nameOfCity isEqualToString:#"New York"] )
selectedController = [[NewYorkViewController alloc] init];
else if( [nameOfCity isEqualToString:#"Chicago"] )
selectedController = [[ChicagoViewController alloc] init];
else if( [nameOfCity isEqualToString:#"Miami"] )
selectedController = [[MiamiViewController alloc] init];
else if( [nameOfCity isEqualToString:#"Los Angeles"] )
selectedController = [[LosAngelesViewController alloc] init];
To do this, you will have to create the different viewControllers (Chicago, New York, Los Angeles, Miami) beforehand, but if you have already created them and left them as an object in the NSDictionary, you can use the nameOfCity to grab the viewController object within the NSDictionary that belongs to that city, and then assign the selectedController to that viewController.
But if you are only using the NSDictionary to store the names of the cities, I think that NSArray will be a better option. I created mine in the appDelegate and the names within the NSArray are #define in my class called global.h as I needed the NSArray and names in many different viewControllers, so if you need that as well, you can do the same.
Hope this helps! :)

Detail View filled with plist information

I've been following a tutorial on iOS development - specifically drill-down UITableViews. I have my own custom plist established, but I can't seem to get the DetailViewController to populate with my plist information. I could really use some help here, I'm a bit over my head!
edit: Here's some details...
The app works through a plist-populated RootViewController, which is a UITableView. When there aren't any children left in the plist, it changes to a Detail view:
AppDelegate.m
NSDictionary *tempDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"]];
self.data = tempDict;
RootViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
if(CurrentLevel == 0) { // At the 'root' of the plist
//Initilalize our table data source
NSArray *tempArray = [[NSArray alloc] init];
self.tableDataSource = tempArray;
AppDelegate *AppDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.tableDataSource = [AppDelegate.data objectForKey:#"Rows"];
self.navigationItem.title = #"PedalTome";
}
else
self.navigationItem.title = CurrentTitle;
}
later on...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//Get the children of the present item.
NSArray *Children = [dictionary objectForKey:#"Children"];
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
}
else {
//Prepare to tableview.
RootViewController *rvController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
//Increment the Current View
rvController.CurrentLevel += 1;
//Set the title;
rvController.CurrentTitle = [dictionary objectForKey:#"Title"];
//Push the new table view on the stack
[self.navigationController pushViewController:rvController animated:YES];
rvController.tableDataSource = Children;
}
}
My DetailViewController.m is empty, with the exception of a placeholder self.navigationController.title.
If I'm understanding correctly, I need to pass information from RootViewController to DetailViewController - the location and implementation of the plist, the index level (is that what it's called) in the plist, and the string inside that index level (under the key Detail).
At this point, any progress is amazing progress. Thanks in advance.
You can pass whatever information you need to your DetailViewController by setting up a synthesized property in your DetailViewController, and then passing your data to it inside your if-block.
For example, in your DetailViewController.h you would have the following (without ARC):
#property (retain, nonatomic) NSDictionary *myAwesomeDictionary;
Or, with ARC enabled:
#property (strong, nonatomic) NSDictionary *myAwesomeDictionary;
Then in DetailViewController.m you would have the following:
#synthesize myAwesomeDictionary;
Then you would change your code block to the following:
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
[dvController setMyAwesomeDictionary:dictionary];
[self.navigationController pushViewController:dvController animated:YES];
}
This assumes that the NSDictionary called dictionary that you created a few lines above is the data that you'd like to show in your DetailViewController.
Then in your DetailViewController's viewDidLoad: method you can access that dictionary using self.myAwesomeDictionary and do whatever you need to do with it.
Disclaimer:
Two things seem to go against Apple's code style standards in your code:
Your AppDelegate stores your model (your plist). - Apple says that you shouldn't crowd your AppDelegate with global data/logic. In general, only write code that pertains specifically to a class, in that specific class.
You aren't parsing your plist into custom objects. - This makes it hard to code because you constantly have to figure out what your generic Array and Dictionary objects represent, and make your code totally unreadable for other people.
Some of your instance variable names are capitalized. For example, NSArray *Children should be NSArray *children and CurrentLevel should be currentLevel. Only Class names have the first letter capitalized.
Check out http://jlawr3nc3.github.com - specifically my CompanyRecords example code for information on how to make a class and FunWithArrays for how to parse a plist into custom objects. MusicLibraryiOS then delves into how to take a plist, parse it into custom objects, and then display it in a UITableView along with a detail view.
Table View Specifier May do what you need.
Specified Table View is an iOS table view that has its contents specified
via a plist file. Its purpose is largely demonstrative but is also designed
to be used in a live product (useful for credits pages). Can be used with
iOS version 4.2 and above.
A dig through their code will most likely be enlightening.

Setting NSDictionary object as a property in child view either leaks when not released or gives EXC_BAD_ACCESS error when released

I have the following problem: In a certain view controller I have a NSDictionary, which itself is an entree in an NSArray object. This view controller has a child view which displays some of the key value pairs that are in this dictionary. Since I need only some key value pairs, I construct a new dictionary object from which I then remove the key value pair I do not want to have in it. To be able to access this dictionary in the child view, I though it would be possible to just set the dictionary via a property, which seems to work fine. To illustrate with some code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
...
// today is an instance of NSArray holding a number of NSDictionary objects
NSDictionary *completeData = [self.today objectAtIndex:row];
NSDictionary *data = [[NSDictionary alloc] initWithDictionary:completeData];
[data removeObjectForKey:#"name"];
SomeViewController *childController = [[SomeViewController alloc] init];
childController.data = data;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
// This results in a EXC_BAD_ACCESS error when navigating back to the parent
// view and calling didSelectRowAtIndexPath a second time. When commenting this
// line out, the error dissapears, but now the object leaks
[data release];
}
The problem arises when, after returning to the parent view, I try to replace the NSArray object (today) by an updated version of itself by calling
- (void)refreshDataNotification:(NSNotification *)notification {
if (notification) {
self.today = [NSArray arrayWithArray:[[[MyAppDelegate getAppDelegate] todaySchedule]
objectForKey:#"data"]];
[self.tableView reloadData];
}
}
Note that as long as I do not release 'data' in didSelectRowAtIndexPath I get no error, but then the object leaks. When I do release it, I receive an EXC_BAD_ACCESS when refreshDataNotification is executed.
If someone has any clue as to what I might be doing wrong, then please do share with me.
Set the environment variable NSZombieEnabled to YES to get more helpful error messages about over releasing objects. (Set the environment variable by viewing details under 'Executables')
Also, it would be helpful to see how you've defined your properties. (e.g. what is the #property for data in SomeViewController?)
ps - I know you haven't pasted actual code, but data is a terrible instance name for an NSDictionary. dict is better - but something more descriptive would make your code easier to understand.

Resources