viewDidLoad called before applicationDidFinishLaunching - ios

I have a problem that I'm not able to solve by myself. I have to download data from my Web server before the user can see the View(UITableViewController), so at the time that the UITableView should be visible, it should have the content to show. The problem is, I start downloading the data in applicationDidFinishLaunching, I have NSLogs in viewDidLoad and in applicationDidFinishLaunching, the funny thing is that the NSLogs of the viewDidLoad are called before the NSLogs of the applicationDidFinishLaunching (those NSLogs are wrote just after all the data is downloaded).
Once i finish downloading the data, i have an NSMutableArray that i pass from the AppDelegate to the TableViewController, but the tableViewController appears totally empty because the ViewDidLoad is called before all the data has been downloaded.
Then I have 1 question: Where i should download all the data before the TableView is loaded?
Solution that I´ve been thinking about: I thought about creating another ViewController(It would be the main View) with an UIImageView that have the image " Loading... ": There I download all the data that I need and I store all the Data to an NSMutableArray. After the data is downloaded, I call the UITableViewController and i pass the NSMutableArray to the UITableViewController. I am sure this solution will work, but it has to be an easier way to solve this.

A good solution would be create a delegate method that tells your ViewController when the download finished. And on the implementation of the method, you call [yourTableView reloadData].
On the class you will download data (on YourDownloadClass.h):
#protocol WebServiceConsumerDelegate
//#required
-(void) requestDidFinishSuccessfully:(NSArray*) resultArray;
#end
#interface YourDownloadClass : UIViewController
#property (strong, nonatomic) id <WebServiceConsumerDelegate> delegate;
And on YourDownloadClass.h just after you download data:
[self.delegate requestDidFinishSuccessfully:resultArray];
Now, go to your TableViewClass.h
#import "YourDownloadClass.h"
#interface TableViewClass : UITableViewController <WebServiceConsumerDelegate>
Finally, on TableViewClass.m:
-(void)viewDidLoad{
[super viewDidLoad];
YourDownloadClass* delegateClass = [YourDownloadClass alloc] init];
// Or if you are downloading on AppDelegate:
AppDelegate* delegateClass = [UIapplication sharedApplication];
delegateClass.delegate = self;
}
Finally, implement the method:
-(void)requestDidFinishSuccessfully:(NSArray*) resultArray{
//Get the objects of resultArray to use on your table view
[yourTableView reloadData];
}
I hope it helps. :)

Related

_tableView reloadData not working when i called method from another class

This table view is working fine when tableview is firstly loaded
but when I tried to reload data using [_tableView reloadData], suddenly list won't reload at all.
Here's code:
-(void)loadListLoop{
AppDelegate* delegate = [[UIApplication sharedApplication] delegate];
shuffleLbl.hidden=false;
[self.view bringSubviewToFront:shuffleLbl];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString*playlistID=delegate.gPlaylistID;
NSString*token=[ud stringForKey:#"youtube_token"];
NSString *origin = [NSString stringWithFormat: #"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=%#&key=AIzaSyBeFK_llQHRl7TyXoQxGkLDmIfKGzOPezM&access_token=%#",playlistID,token];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:origin]];
NSData *json = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSArray *array = [NSJSONSerialization JSONObjectWithData:json options:NSJSONReadingAllowFragments error:nil];
_titleList=[array valueForKeyPath:#"items.snippet.title"];
_thumbnailList=[array valueForKeyPath:#"items.snippet.thumbnails.default.url"];
_idList=[array valueForKeyPath:#"items.snippet.resourceId.videoId"];
NSLog(#"%#",_titleList);
[[NSRunLoop currentRunLoop]runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
dispatch_sync(dispatch_get_main_queue(), ^{
[_tableView reloadData];
});
}
The method loadListLoop is called from another class using:
PlaylistDetailViewController *playlistDetail = [[PlaylistDetailViewController alloc] init];
[playlistDetail loadList];
Looks like loadListLoop is successfully called and everything before [_tableView reloadData]; is also successfully loaded.
I put NSLog inside - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
to see is app is at least trying to reload data but it seems its not working at all.
EDIT:
first,view controller that contains "-(void)loadListLoop" is container view.so target view controller should be on screen
EDIT2:
i defined outlet at .h file below
#import <UIKit/UIKit.h>
#interface PlaylistDetailViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>{
//IBOutlet UITableView *tableView;
IBOutlet UIButton *shuffleLbl;
}
-(void)exitLoopVoid;
-(void)loadListLoop;
#property (nonatomic,strong) IBOutlet UITableView *tableView;
#property (nonatomic,retain) NSMutableArray *titleList;
#property (nonatomic,retain) NSMutableArray *authorList;
#property (nonatomic,retain) NSMutableArray *thumbnailList;
#property (nonatomic,retain) NSMutableArray *idList;
-(IBAction)shuffle;
#end
IMPORTANT
EDIT3:
looks like some thing very weird is happening to NSMutableArray.
overwrite NSMutableArray at loadListLoop method(works fine and checked content with NSLog)
reload table(seems this is working fine too)
content inside NSMutableArray will rollback to old content
anyone has idea about this issue?
EDIT4:
1.overwrite NSMutableArray at loadListLoop method(success)
2.reload table and NSMutableArray will be null only at this method
3.rollback to data that i overwrites data at loadListLoop
I see a couple of problems.
First, the code you posted is a method loadListLoop. But the code that you posted is calling a different method loadList. The two are not connected.
Second, you say:
the method "loadListLoop" is called from another class using
PlaylistDetailViewController *playlistDetail =
[[PlaylistDetailViewController alloc] init];
[playlistDetail loadList];
That code is very wrong. It is creating a brand new instance of PlaylistDetailViewController that is not on-screen, and invoking the loadList method on that newly created view controller.
The view controller hasn't had a chance to display itself yet, so it's view properties will be nil. Plus, the view controller probably doesn't have any data in the model structure it uses to populate it's table view, so it won't have anything to display.
Further, if your view controller's view structure is defined in a storyboard, you can't use alloc/init to create new view controller instances.
At the point where you're trying to call loadList/loadListLoop, is the target view controller on screen? You need to explain your calling sequence.
use NSNotificationCenter for Update the table from one class to another class
1.do this in from where you have to call.
[[NSNotificationCenter defaultCenter] postNotificationName: #"UpdateTable" object:nil userInfo:userInfo];
2.And use this one for table view class. Write this in ViewDidLoad method.
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(refresh_method)
name:#"UpdateTable"
object:nil];
3.
-(void)refresh_method
{
//reload table here.
}

ObjectiveC - [self.view viewWithTag] returning null

I am completely stumped and have been researching for days. Probably something really simple that I am missing.
I have a ViewController which contains a custom UIView called GameView, and a UIView called buttonBox which contains a "next level" button. What I am trying to achieve is when the level is completed in GameView, it fires a function in my ViewController which shows the buttonBox so the user can click the "next level" button. It simply will not work.
I have attempted this in 3 ways, neither have worked:
Creating an IBOutlet in the ViewController, connecting it to the hidden UIView (and it was definitely connected) and calling setHidden:NO.
Calling the [self.view viewWithTag:xxx] and then calling setHidden:NO.
Using hidden=NO instead of setHidden:NO.
Relevant code for ViewController as follows:
#interface PlayViewController : UIViewController
#property GameView *gv;
#property (strong, nonatomic) IBOutlet UIView *buttonBox;
-(void) showButtonBox;
#end
#implementation PlayViewController
#synthesize buttonBox;
...
- (IBAction)showButtonBox {
UIView *uiv = (UIView*) [self.view viewWithTag:999];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Showing box function");
NSLog(#"%#", uiv);
uiv.hidden = NO;
});
}
#end
And my custom view:
#implementation GameView
...
dispatch_async(bgQueue, ^{
_loopRunning = true;
//NSLog(#"Calling main loop...");
while ([self loopRunning])
{
...
PlayViewController * pvc = [[PlayViewController alloc]init];
[pvc showButtonBox];
...
}
#end
The thing is, the variable uiv is returning null in NSLog, which is obviously why hidden is not working, but I have no idea why. It also didn't work when I was using IBOutlet.
Also, current output from NSLog is as follows:
2015-11-24 00:18:38.612 ib[12579:1264539] Showing box function
2015-11-24 00:18:38.612 ib[12579:1264539] (null)
Thanks in advance.
Correct Answer:
The problem was that I was using StoryBuilder to build my UI, but by using the alloc init method was creating a new view controller (which is never shown) instead of correctly referencing the view controller which was being displayed. This is achieved by passing the view controller being displayed to the view in the viewDidLoad function, see below:
#implementation PlayViewController
#synthesize buttonBox;
#synthesize gv;
- (void)viewDidLoad
{
[super viewDidLoad];
gv = [self.view viewWithTag:777];
[gv setPlayViewController:self];
}
...
Man, it's simple. Let's take a look at:
#implementation GameView
...
dispatch_async(bgQueue, ^{
_loopRunning = true;
//NSLog(#"Calling main loop...");
while ([self loopRunning])
{
...
PlayViewController * pvc = [[PlayViewController alloc]init];
[pvc showButtonBox];
...
}
#end
Here we have the issue:
dispatch_async(bgQueue, ^{
I assume, bgQueue stands for "background queue", which means this is not served by the main thread (the UI thread).
Having that said, it's quite naive to expect
[pvc showButtonBox];
to work properly. Just move this code into the main thread. For instance, you can just wrap the aforementioned line of code into a dispatch_async on the main queue. That should solve your probem, if your outlets and/or tags are OK. Cheers.
[[PlayViewController alloc]init];
This creates a new instance of PlayViewController. Where have you defined your outlets and views?
In a storyboard? You can't use this initialiser - nothing from the storyboard will be picked up, you have to use a segue or initializeViewControllerWithIdentifier:.
In a xib file? Is it called PlayViewController.xib? If not, it won't be picked up by the initialiser. Plain alloc/init of a view controller will only find a nib file as described in the documentation of the nibName property.
Do you really want alloc / init at all? Do you actually want to make a new view controller, or is one already on the screen?
From your comments it seems option 3 is the right answer. The PlayViewController is already on the screen, alloc/init is creating a new instance of it, which is never being put on screen, which never loads any views regardless of storyboards or nibs.
You need to get a reference to the existing instance of PlayViewController. Without knowing the structure of your app it's not too easy to say how that's done - is it presenting the game view? Is the game view a subview of the view controller's view? You may need to pass in a reference (weak) to the game view when it is created, at viewDidLoad, or set up an outlet in the storyboard.

iOS singleton viewDidLoad empty and on viewDidAppear not

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

Where to initialise an object

I have a UITableView in my main view and an 'add' button in a UINavigationBar that will push to another view that allows the user to add another object to the tableview. I have a protocol in this view that allows the information to be sent back to the main view.
My problem is that whenever I try to add this new object sent from the protocol (which is a NSMutableDictionary) to a NSMutableDictionary property in the main view, it does not add. I have tried adding an NSLog and it says that this object is null. If I initialise this object in the viewDidLoad method, it will run whenever the UINavigationController pops the view, and resets everything in the dictionary. I do not know where to initialise the object to make sure that it keeps everything stored in it.
The protocol works fine, but I cannot do anything with the object it sends.
In AddCellViewController.h:
#import <UIKit/UIKit.h>
#protocol AddCellDelegate <NSObject>
#required
-(void)passCellInfo:(NSMutableDictionary *)cellInfo;
#end
#interface AddCellViewController : UITableViewController <UITextFieldDelegate> {
id <AddCellDelegate> delegate;
}
#property (strong) id <AddCellDelegate> delegate;
#end
in AddCellViewController.m (the method that utilises the protocol):
-(void)sendObject{
[[self delegate] passCellInfo:newCellInfo];
[self.navigationController popToRootViewControllerAnimated:YES];
}
in MainView.m:
-(void)passCellInfo:(NSMutableDictionary *)cellInfo{
[self.cellInformation setValue:cellInfo forKey:[cellInfo objectForKey:#"cell_title"]];
[self.cells addObject:[cellInfo objectForKey:#"cell_title"]];
[self.tableView reloadData];
NSLog(#"%#: cellInfo - cellInformation: %# - cells: %#",cellInfo ,self.cellInformation,self.cells); //logs the object passed from the protocol (this works), the cellInformation object, and cells object (these return null)
}
You can use a NSMutableDictionary property in a static or global class, and access it very easy from the tableview or any other view:
You feed your table from that dictionary
And you add elements from your other view into that dictionary (the protocol is not needed anymore).
Everytime your tableView appears, you should refresh the data of the table from this global class.
Some example of how to use it:
How to implement global static class
In your code you do this. You using same key every time. So it will get replaced.
[self.cells addObject:cellInfo];
I will tell you a simple way instead. Send your mutableDictionary to secondView from init method. Copy in class level mutableDictionary. Do not allocate or initialize. Add new item in that Dictionary. It will reflect in mainView dictionary. Call [tableView reloadData] in mainView viewWillAppear method.

How to pass data to parent view IOS

I know this question is asked once every two days. I can not see what I am doing wrong though.
I have a storyboard navigation controller based app.
My notification and pop / push segues works well, only thing is I can not add string to parents view NSmutablearray.
I want to add a string object to parent view's nsmutablearray. My decent code does not pass any data.
parent.h
#interface CreaatePlistTableViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource>{
NSMutableArray *presenterList;
}
#property (nonatomic, strong) NSMutableArray *presenterList;
parent.m
NSString * const NOTIF_CreatePlist_UpdateTableview= #"CreatePlist/UpdateTableview";
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Private interface definitions for update tableview
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#interface CreaatePlistTableViewController (private)
- (void)CreatePlistUpdateTableview:(NSNotification *)notif;
#end
#implementation CreaatePlistTableViewController
#synthesize presenterList=_presenterList;
- (void)viewDidLoad
{
[super viewDidLoad];
_presenterList=[[NSMutableArray alloc] init];
// Register observer to be called when logging out
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(CreatePlistUpdateTableview:)
name:NOTIF_CreatePlist_UpdateTableview object:nil];
NSLog(#"Presenter List: %#", _presenterList);
}
- (void)CreatePlistUpdateTableview:(NSNotification *)notif{
NSLog(#"Notification recieved");
NSLog(#"Presenter List: %#", _presenterList);
[_createPlistTableview reloadData];
}
child.h
#interface AddPresenterViewController : UITableViewController<UITextFieldDelegate,UIAlertViewDelegate>{
CreaatePlistTableViewController *crereaatePlistTableViewController;
}
#property(nonatomic,strong) CreaatePlistTableViewController *crereaatePlistTableViewController;
child.m
#synthesize crereaatePlistTableViewController=_crereaatePlistTableViewController;
//finished adding presenter
-(IBAction)finishedAddingPresenter:(id)sender{
//some xml string here
NSLog(#"final result XML:\n%#", writer.XMLString);
_crereaatePlistTableViewController=[[CreaatePlistTableViewController alloc]init];
//add object to parents view data source
[_crereaatePlistTableViewController.presenterList addObject:writer.XMLString];
//dismiss the view
[self.navigationController popViewControllerAnimated:YES];
//notify the parent view to update its tableview
[[NSNotificationCenter defaultCenter] postNotificationName:#"CreatePlist/UpdateTableview" object:nil];
}
Output
Notification recieved
Presenter List: (
)
So notification works when I click the button. But it does not pass object to nsmutablearray.
What I am doing wrong here ? How can I add an object to parent view's nsmutablearray?
It seems everything is good except your alloc of parent view object I am not that familiar with storyboard but You said you are using navigation navigation controller
so change this
_crereaatePlistTableViewController=[[CreaatePlistTableViewController alloc]init];
to
_crereaatePlistTableViewController= [self.navigationController.viewControllers objectAtIndex:0];
It may work I am not sure
You wrote this.
[_crereaatePlistTableViewController.presenterList addObject:writer.XMLString];
Do you ever initialize the array? No. Use the debugger and you will see that at this line the presenterList is nil.
Now as a point of style. Avoid using NSNotificationCenter to pass data or signaling other objects. #TheRonin gave a handy link. You should also look into some tutorials on Segues, because these are solved problems.
This is another related post that you might find interesting.

Resources