_tableView reloadData not working when i called method from another class - ios

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.
}

Related

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

viewDidLoad called before applicationDidFinishLaunching

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. :)

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.

ios ARC table bad access

for my table view I have the following going on (paraphrased)
.h
#interface OptionsView : UIView <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, retain) NSArray *dataSource;
.m
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
...
self.dataSource = [NSArray arrayWithObjects:options, sections, sponsor, nil];
}
return self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(self.dataSource) {
NSArray *ds = [self.dataSource objectAtIndex:indexPath.section];
NSDictionary *d = [ds objectAtIndex:indexPath.row];
ActionBlock a = [d objectForKey:ACTION]; // <-- Thread 1: EXC_BAD_ACCESS (code=2, address=0x802)
if(a != nil) a();
}
}
You can see that in my didSelectRowAtIndexPath I'm getting an EXC_BAD_ACCESS but I'm not sure why. Because I'm using arc it isn't a zombie problem (already checked).
Using breakpoints I see that the self.dataSource exists after it's initialized, but not later when it's needed. It doesn't show up as <nil> either, it's just white space. Also, this works in debug but not in release so what does that mean?
** EDIT adding a screenshot **
You will notice that neither ds or d show up.. odd?
So there's two places I can see that might cause the problem. First, what is ACTION? Is it some sort of NSString? Just want to make sure that you're using a valid key object. Second, (and more likely the problem), it looks like ActionBlock is some kind of code block you're storing in a collection array. Are you copying that block before you store it in the array/dictionary? You must copy any block you intend on keeping around (storing) longer than the scope it was created in. This is easy to do. For example:
void (^NewBlock)(void) = [^{
....code....
} copy];
Alternately:
void (^NewBlock)(void) = ^{
....code....
};
[dictionary setObject:[NewBlock copy] forKey:ACTION]; //Assuming #define ACTION #"action"
This copies it onto the heap so that it can be stored. If you don't copy it, you'll get BAD_EXC_ACCESS anytime you try to reference the block outside the scope it was created in.
It seems you´re using a UIViewController instead of a UITableViewController. If I remember correctly, you have to go to the inspector and drag the delegate and datasource from the tableView to the UIViewController. I don´t remember exactly and I´m not on my computer but I´m sure I did it several times before I began to use a UITableViewController for every tableView I have.
Oh, and i wouldn´t use dataSource as a name for your array. Just to prevent naming conflicts.

Proper way of creating new objects which are copies of NSDictionary and NSArray objects defined in app delegate

I am wondering what the correct way is to make a copy of an object defined in the app delegate or a singleton object. In short, I am making an app which requires a user to login. This login view is just a modal view controller on top of the 'real' app, which consists of a tabbarcontroller, plus some tableview controllers. After a successful login, there is send a data request to a remote server, and the modal view controller is dismissed, revealing the tabbar controller and table views holding the XML data. To parse the incoming data, I have created a singleton object named DataParser, which has interface
...
#interface DataParser : NSObject {
// Data objects that hold the data obtained from XML files
NSMutableDictionary *personnel;
NSMutableDictionary *schedule;
NSMutableDictionary *today;
}
#property (nonatomic, retain) NSMutableDictionary *personnel;
#property (nonatomic, retain) NSMutableDictionary *schedule;
#property (nonatomic, retain) NSMutableDictionary *today;
...
Now in these dictionaries I store (mutable) dictionaries and arrays holding NSString objects with the parsed XML data. Since I do not want to modify these original objects holding the parsed data (that is to say, I only want to modify them at the login stage, but not in any of the tableview controllers), I am creating a new dictionary object which holds a copy of the content of one of the dictionaries above in each tableview controller. So for instance, in the loadView of a view controller called ScheduleViewController I have
...
#interface ScheduleViewController : UITableViewController {
NSDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [NSDictionary dictionaryWithDictionary:sharedSingleton.schedule];
}
...
Now this seems to work fine. The only difficulty arises however, when the user 'logs out', which entails popping the login modal view controller back on the stack. When the user presses the login button again, then a new XML data request is send to the server and the dictionaries in the singleton object get refreshed with the (new) data (I check if they contain any data, if so I call removeAllObjects before filling them up again with newly parsed data). At this point the dictionaries in all view controllers should be updated too, however I am not quite sure how to go about this the right way. I have noticed that loadView is not always called again in this case and so to this end I have added the same code as above in loadView to every viewWillAppear method. After navigating back and forth between the different views or navigating back and forth between child views of a tableview a couple of times, I receive an EXC_BAD_ACCESS error however. I suspect this has to do with not properly retaining the copies of the original dictionaries, but I don't seem to be able to find a solution around this. Instead of using dictionaryWithDictionary, which I suspect is not the right way to go anyway, I also tried a different approach, where instead of using objects of type NSDictionary in ScheduleViewController I use NSMutableDictionary. So:
...
#interface ScheduleViewController : UITableViewController {
NSMutableDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSMutableDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [[NSMutableDictionary alloc] initWithDictionary:sharedSingleton.schedule];
}
- (void)viewWillAppear {
DataParser *sharedSingleton = [DataParser sharedInstance];
[self.copyOfSchedule removeAllObjects];
[self.copyOfSchedule addEntriesFromDictionary:sharedSingleton.schedule];
[self.tableView reloadData];
}
...
But this doesn't get rid of the EXC_BAD_ACCESS errors. To make a very long story short: what would be the best way to go about making independent copies of objects defined in a singleton object or app delegate and which can be dynamically updated at request? Since I am already rather into the project and lots is going on, I realize that my question may be a bit vague. Nonetheless I hope there is somebody who could enlighten me somehow.
Deep copies are often made recursively. One way to do it would be to add -deepCopy methods to NSDictionary and NSArray. The dictionary version might go like this:
- (NSDictionary*)deepCopy
{
NSMutableDictionary *temp = [self mutableCopy];
for (id key in temp) {
id item = [temp objectForKey:key];
if ([item respondsToSelector:#sel(deepCopy)] {
// handle deep-copyable items, i.e. dictionaries and arrays
[temp setObject:[item deepCopy] forKey:key]
}
else if ([item respondsToSelector:#(copy)]) {
// most data objects implement NSCopyable, so will be handled here
[temp setObject:[item copy] forKey:key];
}
else {
// handle un-copyable items here, maybe throw an exception
}
}
NSDictionary *newDict = [[temp copy] autorelease];
[temp release]
return newDict;
}
I haven't tested that, so be a little careful. You'll want to do something similar for NSArray.
Note that views are not copyable.
It is quite a typical pattern that you build an array or dictionary with some code, so clearly it must be mutable while you add bits to it, and when you're done you don't want it ever to change. To do this:
Have a property like
#property (...) NSArray* myArray;
When you calculate the contents of myArray, use a mutable array to build it, like
NSMutableArray* myMutableArray = [NSMutableArray array];
When you're done building the array, just use
self.myArray = [NSArray arrayWithArry:myMutableArray];

Resources