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.
Related
I have a tabBarView which have two tableViews. each of these tableViews will represent some news from a remote server. I want to populate tableView's datasource when tableViewController's init method is called. so I have put the needed networking operation inside init method. My init method is this:
- (instancetype) init{
self = [super init];
[NewsManager fetch:10 remoteNewsOfLanguage:#"fa" withOffsett:1 andCompletionHandler:^(NSMutableArray *news) {
self.newsList = news;
}];
self.tabBarItem.title = #"my title";
return self;
}
newsList is an array holding news loaded from server.
But when I run my project the order of invocation for tableViewController's methods is like the following:
tableViewController's init method is called and finished (but the completion handler block is not called yet)
tableViewController's viewDidLoad method is called ( it is called when the tableViewController is added to tabBarView's viewControllers array)
tableViewController's delegate method tableView:numberOfRowsInSection is called
the network operation's completionHandler block is called and the newsList array is set to the retrieved news from server
So my problem is that before my newsList array is populated the method tableView:numberOfRowsInSection is called and so my tableView is not filled with any news. How should I solve this issue?
Thanks
you should reload table data after you get data from server. then only your table will show updated data.
[NewsManager fetch:10 remoteNewsOfLanguage:#"fa" withOffsett:1 andCompletionHandler:^(NSMutableArray *news) {
self.newsList = news;
[yourTableview reloadData];//add this line
}];
The added line does the job and makes the new data to be loaded in the tableView but there is a small point that I think you should consider
[tableView reloadData]
will be executed in a thread other than mainThread and this will cause a 5 to 10 seconds delay for the data to be loaded on the tableView.
to prevent this you should somehow tell it to run the reloadData method on the main thread. this is done with the dispatch_async. So you should call [tableView reloadData] like this:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
I created a singleton in ios7 like this:
SharedData.h
#interface SharedData : NSObject
{
}
+ (id)sharedInstance;
#property (strong, nonatomic) NSMutableArray *list;
#end
SharedData.m
#import "SharedData.h"
#implementation SharedData
#synthesize list;
// Get the shared instance thread safe
+ (SharedData *)sharedInstance {
static dispatch_once_t once = 0;
static SharedData *sharedInstance = nil;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
//initialize
list = [[NSMutableArray alloc] init];
}
return self;
}
#end
I always use this code to access this class:
SharedData *sharedData = [SharedData sharedInstance];
The problem is now when I switch the view in my viewDidLoad method the list is empty but in my viewDidAppear method everything is fine. Any ideas?
EDIT:
This is the code how I change the views:
SharedData *sharedData = [SharedData sharedInstance];
//clear feed and add new feed
[sharedData.list removeAllObjects];
[sharedData.list addObjectsFromArray:newList];
//show new gui
[self.navigationController performSegueWithIdentifier:#"goToMain" sender:self];
NOTE: I push from a normal ViewController to a TabBarController -> NavigationController -> TableViewController to display the list.
I guess you have the confusion between these two viewcontroller methods:
-(void)viewDidLoad{
//
}
&
-(void) viewDidAppear{
//
}
viewDidAppear is the method which is called each time your view changes but viewDidLoad is the method which is not necessarily called each time your view changes.
ViewDidLoad method is called when view loads for the first time, after that it doesn't get called until the views are removed/released.
P.S: I suggest you to put the breakpoint in your viewDidLoad and viewDidAppear method and feel it. Your answer lies there.
Hope this helps you alot.
Good Luck.
The problem was i created a segue which went from the button to the next view. Because of this the viewDidLoad gets earlier called than the list assigned. I just changed the segue to go from view to view.
How are you changing from one viewController to the other? Wich classes are the parents of your destination ViewController?,
If you are modifying properties of the view in the prepareForSegue method... you are forcing the view to load.
For example, you are setting the list of your singleton in prepareForSegue, but before setting the list you are modifying a property of your destination viewController. (doing something like destVC.view = XXX or destVC.viewControllers = XX if you are subclassing a UITabBarViewController...) Then you are triggering the viewDidLoad method , and it's executing before you have set the list to the correct value.
Or maybe you are seguing in two different places to the destinationViewController. And when the viewDidLoad happens, you still have not updated the list on the singleton.
Here is the transcription of the chat with the poster of the question: https://chat.stackoverflow.com/transcript/55218
I have a custom NSObject class, doing some FTP work (display, transfer...).
This FTP class has a downloadFiles: method, which is launch inside a NSOperationQueue.
And I would like that method to provide some results as a data source in a UITableView. So I decided to delegate those results to the UITableViewController.
So the protocol for the FTP class :
#protocol FTPDelegate;
..
#interface FTP...
#property (nonatomic, weak) id <FTPDelegate> delegate;
...
#protocol FTPDelegate
-(void)FTPMessageReceived: (NSString *)message;
#end
Then I declared the custom UITableViewController as delegate of the FTP class:
self.myFTP = (FTP *) [self FTPManager];
self.myFTP.delegate = self;
So when the downloadFiles: method from the FTP class wants to display some message in the UITableView, it calls:
[[self delegate] FTPMessageReceived:#"dir created"];
The UITableViewController implements that method :
-(void)FTPMessageReceived: (NSString *)message {
NSUInteger index;
NSArray * indexArray;
[[self logLine] addObject: message]; // logLine is my data source
index = [[self logLine] count]-1;
NSLog(#"%u", index);
indexArray = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:index inSection:0]];
[[self logView] insertRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationRight];
}
The insert works, but the new row takes around 5 seconds to be displayed in the UITableView !!
And when I put some log inside the tableView:cellForRowAtIndexPath: method, it's display immediately.
And when I put the FTPMessageReceived code inside a button (for example), the UITableView displays the new row immediately !
Any suggestion?
It sounds like FTPMessageReceived: is being called on a background thread. Try calling it on the main thread with:
dispatch_async(dispatch_get_main_queue(), ^{
[[self delegate] FTPMessageReceived:#"dir created"];
});
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.
I have a view which has a UITableView where I am lazy loading images. I have a class called ThumbDownloader where I initialize an NSURLConnection and upon finishing loading the image when connectionDidFinishLoading is called, within connectionDidFinishLoading, I make a call like this back to my main view:
[delegate appImageDidLoad:self.indexPathInTableView];
In my main view I have an array of ThumbDownloader instances. The array is named: imageDownloadsInProgress
The problem is, if I enter the view and quickly exit it before all of the images are done downloading, I get the zombie:
-[myApp appImageDidLoad:]: message sent to deallocated instance 0xa499030
I have tried a bunch of ways to release the ThumbDownloader instances in dealloc and such, but nothing seems to work.
Here is where I set up the instance and add it to the array:
- (void)startIconDownload:(Product *)product forIndexPath:(NSIndexPath *)indexPath
{
ThumbDownloader *thumbDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (thumbDownloader == nil)
{
thumbDownloader = [[ThumbDownloader alloc] init];
thumbDownloader.product = product;
thumbDownloader.imageSizeWidth = 87;
thumbDownloader.imageSizeHeight = 87;
thumbDownloader.indexPathInTableView = indexPath;
thumbDownloader.delegate = self;
[imageDownloadsInProgress setObject:thumbDownloader forKey:indexPath];
[thumbDownloader startDownload];
[thumbDownloader release];
}
}
Make sure you clear the delegate on the NSURLConnection.
Just add
[connection cancel]
in the dealloc method of ThumbDownloader class. This will cancel the ongoing download and will prevent the message from being called.