I am pretty stuck with this and hoping someone can show me some light about this.
I need help to figure out how to refresh the table list on the main view from the slide-in menu.
IN MAIN VIEW
So basically, I have a main view that has a list of events. I use this function to retrieve data from the database:
[self loadEvents:eventCat sortEvent:nil searchDate:dateSelected];
Inside that function, it has the script that gets data, determines if there is content and then reloads the table:
- (void)loadEvents:(NSString*)searchType sortEvent:(NSString*)sortType searchDate:(NSString *)dateSelect{
NSString *url = [NSString stringWithFormat: #"http://www.url.com/eventList.php];
NSURL *eventURL = [NSURL URLWithString: url];
NSLog(#"%#", eventURL);
NSData *jsonData = [NSData dataWithContentsOfURL: eventURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error: &error];
self.eventList = [NSMutableArray array];
NSArray *eventArray = [dataDictionary objectForKey:#"fitpass"];
//NSString *eventFound = [dataDictionary objectForKey:#"notfound"];
//if nothing is found
if ([dataDictionary objectForKey:#"notfound"]){
tableView.hidden = YES;
noEvent.hidden = NO;
NSLog(#"123");
}else{
noEvent.hidden = YES;
tableView.hidden = NO;
NSLog(#"RELOADDATA");
for(NSDictionary *eventDictionary in eventArray){
Event *event = [Event blogPostWithTitle:[eventDictionary objectForKey:#"name"]];
event.eventTime = [eventDictionary objectForKey:#"event_time"];
[self.eventList addObject:event];
[tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
}
I tried using the NSNotificationCenter method to refresh the data. So I have this in the viewDidLoad Method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshTableWithNotification:) name:#"RefreshTable" object:nil];
Then this function right after ViewDidLoad Method:
- (void)refreshTableWithNotification:(NSNotification *)notification
{
[self.tableView reloadData];
}
IN THE SLIDE MENU VIEW
In the side menu, I have a table list to populate the different categories, and when use click a category, it should refresh the event list in the main view:
//on direct press, trigger segue
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//get current value and set the eventCategory ID in userDefault
NSDictionary *catPicked = [self.catList objectAtIndex:indexPath.row];
NSString *eventCat =[catPicked objectForKey:#"ID"];
NSLog(#"%#Picked:", eventCat);
[userDefaults setObject:eventCat forKey:kEventCat];
[userDefaults synchronize];
NSLog(#"%#entered", [userDefaults objectForKey:kEventCat]);
//redirect back to eventList;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier: #"eventListViewController"];
//refresh data
EventLists *destinationController = [[EventLists alloc] init];
[destinationController loadEvents:eventCat sortEvent:nil searchDate:nil];
//send notofication over
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshTable" object:nil userInfo:nil];
[destinationController.view setNeedsDisplay];
[[SlideNavigationController sharedInstance] popToRootAndSwitchToViewController:vc
withSlideOutAnimation:self.slideOutAnimationEnabled
andCompletion:nil];
return;
}
What I am stuck is that I can see the data (eventList) is updated, but the view does not refresh...
Any help would be very much appreciated!!!
Rory!!!
Thank you so much for your help!! I finally figured it out with your hint.
So basically I thought it was passing he data because of my log. But it was just merely running the function. No data was passed.
And the solution was actually rather simple. Since the data was not really being passed over, refreshing the table in the notification function will not work. The data needs to be reloaded. In other word, I just needed to change the notification function in the Main View to:
- (void)refreshTableWithNotification:(NSNotification *)notification
{
[self loadEvents:[userDefaults objectForKey:kEventCat] sortEvent:nil searchDate:dateSelected];
}
Then my Side Menu View is now much simpler since all I needed was the Notification call and view switch:
//redirect back to eventList;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier: #"eventListViewController"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshTable" object:nil userInfo:nil];
And yes! I needed to move the notification in the Main view to ViewDidAppear:
- (void)viewDidAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshTableWithNotification:) name:#"RefreshTable" object:nil];
}
Thank you so much for your help!!
Related
When a button is clicked at FirstVC, it will pass data and trigger SecondVC using NSNotificationCenter
During initial launch of the app, because SecondVC has not been initialize yet, so data cannot be passed to SecondVC. NSNotificationCenter cannot function properly. Only after SecondVC has been initialize, NSNotificationCenter will function correctly.
So I need to initialise SecondVC somewhere. Will it be at - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions?
Or how do I programatically call the tab of SecondVC.
FirstVC
#import "Search.h"
#import "Classes.h"
#import "MyTabBarController.h"
#interface Search(){
AppDelegate *appDelegate;
CERangeSlider* _rangeSlider;
NSString *sURL, *strResult, *sRemaining, *sStartTime, *sEndTime, *sSelectedLat, *sSelectedLong;
}
#end
#implementation Search
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:sURL forKey:#"theURL"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"toClasses" object:nil userInfo:userInfo];
}
#end
Second VC
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(receiveTestNotification:)
name:#"toClasses"
object:nil];
dtDate = [[NSMutableArray alloc] init]; //=== Mutable array to store the dates generated
self.currentPageIndex = 0;
[self setupSegmentButtons];
NSDate *now = [NSDate date];
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/YYYY"];
sDtDate = [dateFormatter stringFromDate:now];
[self LoadClasses];
}
-(void)viewWillAppear:(BOOL)animated {
//--- Hide the Top Navigation Controller Bar at the current View
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}
//--- Top Navigation Controller reappear on the next VC
-(void)viewDidDisappear:(BOOL)animated{
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
-(void) receiveTestNotification:(NSNotification*)notification
{
if ([notification.name isEqualToString:#"toClasses"])
{
NSDictionary* userInfo = notification.userInfo;
NSLog (#"Successfully received userInfo! %#", userInfo);
NSString* sFromSearch = [NSString stringWithFormat: #"%#", userInfo];
NSLog (#"Successfully received test notification! %#", sFromSearch);
}
}
In my opinion, you don't need to use notification or singleton on this case.
Simply, get SecondViewController from self.tabBarController and call the method.
First VC
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
UINavigationController* secondNav = (UINavigationController*)self.tabBarController.viewControllers[1];
SecondViewController* secondViewController = [secondNav.viewControllers firstObject];
[secondViewController handleString:sURL];
}
Second VC
- (void)handleString:(NSString*)string {
// Do whatever you want with string passed from First VC
}
You added observer in viewDidLoad, so it will not work even you create it before user tap on button and send notification. because observer will not be registered. I advise you not use observer to send data in this case. you can save this data elsewhere and use it when seconVC will load. for example in singleton object.
your Singleton object looks like this:
Interface:
#interface DataManager : NSObject
#property (nonatomic, strong) NSDictionary *userInfo;
+ (DataManager *) getInstance;
#end
Implementation:
#implementation DataManager
+ (DataManager *) getInstance {
static DataManager *appManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
appManager = [[DataManager alloc] init];
});
return appManager;
}
#end
Now you can access this object where you want and you can assured that only one instance is created.
here is your button click method:
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:sURL forKey:#"theURL"];
[DataManager getInstance].userInfo = userInfo;
}
and your viewDidLoad in secondVC
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *userInfo = [DataManager getInstance].userInfo;
}
I have implemented iOS slide menu navigation controller in my app and its working fine earlier. But somehow now its left menu view controllers is coming black screen. Please suggest if someone faced this issue. Below code I have mentioned in Appdelegate.m didFinishLaunchingWithOptions.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main"
bundle: nil];
self.navigationController = (UINavigationController*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"navBar"];
self.landingScreen = (SlideNavigationController*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"FirstViewController"];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
LeftMenuViewController *leftMenu = (LeftMenuViewController*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"LeftMenuViewController"];
self.landingScreen = [SlideNavigationController sharedInstance];
self.landingScreen.leftMenu = leftMenu;
// Creating a custom bar button for right menu
[[NSNotificationCenter defaultCenter] addObserverForName:SlideNavigationControllerDidClose object:nil queue:nil usingBlock:^(NSNotification *note) {
NSString *menu = note.userInfo[#"menu"];
NSLog(#"Closed %#", menu);
}];
[[NSNotificationCenter defaultCenter] addObserverForName:SlideNavigationControllerDidOpen object:nil queue:nil usingBlock:^(NSNotification *note) {
NSString *menu = note.userInfo[#"menu"];
NSLog(#"Opened %#", menu);
}];
[[NSNotificationCenter defaultCenter] addObserverForName:SlideNavigationControllerDidReveal object:nil queue:nil usingBlock:^(NSNotification *note) {
NSString *menu = note.userInfo[#"menu"];
NSLog(#"Revealed %#", menu);
}];
Earlier one mistake by replacing the NSLog with //**NSLog is the issue which is replaced the NSlog in the SlideNavigationController.m
//Earlier
if (singletonInstance)
//**NSLog(#"Singleton instance already exists. You can only instantiate one instance of SlideNavigationController. This could cause major issues");
//After removing this //** from the NSLOG now its working fine
if (singletonInstance)
NSLog(#"Singleton instance already exists. You can only instantiate one instance of SlideNavigationController. This could cause major issues");
Thanks to Bond and Keyur to giving their feedbacks.
When I login to my app, my app does push the ViewController XYZMainViewController, XYZMainViewController viewWillAppear:animated call method that makes a request to my API to retrieve the authenticated user data, at this time I update the text of a label to show the user name. When I logout the app, it returns me to the login ViewController, when I do login again with another user, XYZMainViewController label text contains the name of the previous user, without updating the label text.
XYZMainViewController.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self homeProfile];
}
- (void)homeProfile
{
[NXOAuth2Request performMethod:#"GET"
onResource:[NSURL URLWithString:#"http://{url}/users/userinfo"]
usingParameters:nil
withAccount:[XYZCommonFunctions user]
sendProgressHandler:nil
responseHandler:^(NSURLResponse *response, NSData *responseData, NSError *error){
NSDictionary *parsedData = [[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error] objectForKey:#"data"];
_user = [parsedData objectForKey:#"user"];
[self.label setText:[NSString stringWithFormat:#"Welcome %#!", [_user objectForKey:#"username"]]];
}];
}
- (IBAction)logout:(id)sender {
XYZAppDelegate* appDelegate = (XYZAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate logout];
}
XYZAppDelegate.m
- (void)login
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *identifier = [prefs stringForKey:#"accountidentifier"];
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
NSString *viewIdentifier = #"WelcomeView";
if(identifier != nil){
NXOAuth2Account *account = [[NXOAuth2AccountStore sharedStore] accountWithIdentifier:identifier];
if(account != nil) {
viewIdentifier = #"MainView";
}
UIViewController *controller = [mainStoryboard instantiateViewControllerWithIdentifier: viewIdentifier];
[navigationController pushViewController:controller animated:NO];
return;
}
}
- (void)logout
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs removeObjectForKey:#"accountidentifier"];
[prefs synchronize];
for (NXOAuth2Account *a in [[NXOAuth2AccountStore sharedStore] accounts] ){
[[NXOAuth2AccountStore sharedStore] removeAccount:a];
}
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
[navigationController popToRootViewControllerAnimated:YES];
}
I need to reinitialize all data in XYZMainViewController.
Thank you.
Look like problem is related to fetching JSON Object. It is possible that everytime you have send same user to fetch user data. You are not using NSUserdefault object to display name, you are using value, which is return by JSON Object. According to me cause of error is "withAccount:[XYZCommonFunctions user]" line.
I would like to suggest, instead of using
-(void)viewWillAppear:(BOOL)animated {
you can use
- (void)viewDidLoad
so that your login action performed only when your LoginController loads,instead when LoginController appear.
New viewwillAppear look as given below -
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.label setText:#""];
}
and ViewDidLoad -
- (void)viewDidLoad
{
[super viewDidLoad];
[self homeProfile];
}
Also check your json response, whether you are getting response success or error.According to response need to handle.
Hope this helps.
My First View is a UIViewController i have a UITable View in it and it works just fine.
From the first view i push another view, in that view i select a group name then the view dismiss, what i want is when i select the group name from the second view the first view loads the values of that group name to the table view, this is the code i use and its not working
the second view code
- (IBAction)sendNames:(id)sender {
if (!keyValue) {
NSLog(#"Didnt Select Any Group To Send");
return;
}
ViewController *theInstance = [[ViewController alloc] init];
[theInstance setGroupName:keyValue];
[theInstance ReceivedGroup:nil];
[self dismissViewControllerAnimated:YES completion:nil];
//tried that option and didnt work too, i added the rest of the code in the viewDidLoad
//[[NSNotificationCenter defaultCenter] postNotificationName:#"RG" object:nil];
}
keyValue is NSString that been set on tableView didSelectRowAtIndexPath
the first view
- (IBAction)ReceivedGroup:(id)sender {
NSString *path = [self dataFilePath];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *tempArray = [dict objectForKey:groupName];
nameArray = [[NSMutableArray alloc] init];
[nameArray addObjectsFromArray:tempArray];
[nameTable reloadData];
NSLog(#"Group Name : %#",groupName);
NSLog(#"Array : %#",nameArray);
}
groupName is NSString
In the log i get the groupName and the nameArray printed
but the tableView is empty.
EDIT: I fixed the problem and posted my answer
Your viewcontroller "theInstance" will be deallocated immediately after calling sendNames: so nothing's gonna happen. You should rather pass a pointer of your first viewcontroller to your second viewcontroller (e.g. set it as a property) and perform all operations on this viewcontroller.
subclass UITableViewController
#interface SecondViewController : UITableViewController
#property (nonatomic, strong) ViewController *firstViewController;
#end
if using storyboard segues implement in your first viewcontroller :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((SecondViewController *) segue.destinationViewController).firstViewController = self;
}
sendNames: should then look like this:
- (IBAction)sendNames:(id)sender {
if (!keyValue) {
NSLog(#"Didnt Select Any Group To Send");
return;
}
ViewController *theInstance = self.firstViewController;
[theInstance setGroupName:keyValue];
[theInstance ReceivedGroup:nil];
[self dismissViewControllerAnimated:YES completion:nil];
//tried that option and didnt work too, i added the rest of the code in the viewDidLoad
//[[NSNotificationCenter defaultCenter] postNotificationName:#"RG" object:nil];
}
I Fixed it by sending the keyValue using NSNotificationCenter
Here is my code now
For The Second View
- (IBAction)sendNames:(id)sender {
if (!keyValue) {
NSLog(#"Didnt Select Any Group To Send");
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"RG" object:keyValue];
[self dismissViewControllerAnimated:YES completion:nil];
}
Then On The First View
- (void)receiveNotification:(NSNotification *)notification {
NSString *Key = [notification object];
nameArray = [[NSMutableArray alloc] init];
[nameArray removeAllObjects];
NSString *path = [self dataFilePath];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *tempArray = [dict objectForKey:Key];
[nameArray addObjectsFromArray:tempArray];
[self.nameTable reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[nameTable numberOfRowsInSection:0] - 1 inSection:0];
[nameTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotification:) name:#"RG" object:nil];
}
The App I'm trying to do has a tabbar controller.
When the App starts, I'm getting the user location in the AppDelegate and when I've got the accuracy I need the AppDelegate sends an NSNotification to my App's starting page (index 0 of the tab bar controller).
Upon receiving the notification, this view tries to send an email with the user coordinates and other data, but as soon as the MFMailComposeViewController is presented I get the following error:
Warning: Attempt to present <MFMailComposeViewController: 0x98a0270> on <UITabBarController: 0x988c630> whose view is not in the window hierarchy!
What am I missing?
Thanks.
EDIT: adding some code...
This is what I've got in my AppDelegate.m:
- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSUserDefaults *phoneNumbers = [NSUserDefaults standardUserDefaults];
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 10.0) {
[self locationUpdate:newLocation];
smsLoc = newLocation;
if ([[phoneNumbers objectForKey:#"sendSMS"] isEqualToString:#"yes"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"sendSMS" object:nil];
} else if ([[phoneNumbers objectForKey:#"sendEmail"] isEqualToString:#"yes"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"sendEmail" object:nil];
}
}
}
Then, in my first view controller I have:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sendSMS:) name:#"sendSMS" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sendEmail:) name:#"sendEmail" object:nil];
}
And at the end, the selector for "sendSMS" (the other is pretty similar):
- (void)sendSMS: (NSNotification *)notification {
NSUserDefaults *phoneNumbers = [NSUserDefaults standardUserDefaults];
if ([phoneNumbers objectForKey:#"first"] || [phoneNumbers objectForKey:#"second"]) {
MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init];
if ([MFMessageComposeViewController canSendText]) {
AppDelegate *deleg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
controller.body = [NSString stringWithFormat:#"some message with coordinates %.4f - %.4f", [deleg currentLocation].coordinate.latitude, [deleg currentLocation].coordinate.longitude];
controller.recipients = [NSArray arrayWithObjects:[phoneNumbers objectForKey:#"first"], [phoneNumbers objectForKey:#"second"], nil];
controller.messageComposeDelegate = self;
[self presentModalViewController:controller animated:YES];
}
}
}
}
Second edit: adding some more code.
UITabBarController *tabBarController = [[UITabBarController alloc] init];
tabBarController.delegate = self;
tabBarController.selectedIndex = 0;
[[tabBarController.tabBar.items objectAtIndex:0] setTitle:NSLocalizedString(#"Home", nil)];
[[tabBarController.tabBar.items objectAtIndex:1] setTitle:NSLocalizedString(#"Requests", nil)];
[[tabBarController.tabBar.items objectAtIndex:2] setTitle:NSLocalizedString(#"Account", nil)];
[[tabBarController.tabBar.items objectAtIndex:3] setTitle:NSLocalizedString(#"Settings", nil)];
//some other controls from DB
[[tabBarController.tabBar.items objectAtIndex:1] setBadgeValue:[NSString stringWithFormat:#"%d",number]];
The tabbarController has been made via IB, but I've added the code above in my AppDelegate because I need to localize the tab bar items and to add a badge to one of them.
Am I doing something wrong here?
I'm not sure if you have solve this issue. The error message means the viewcontroller you use to present another modal viewcontroller is not visible on the window. This can happen for e.g:
[VC1 presentModalViewController:VC2];
// Error here, since VC1 is no longer visible on the window
[VC1 presentModalViewController:VC3];
If your issue is like above, you can fix it like:
if (self.modalViewController != nil) {
[self.modalViewController presentModalViewController:VC3 animated:YES];
} else {
[self.tabBarController presentModalViewController:VC3 animated:YES];
}
If that doesn't fix your issue, maybe you can try to present using self.tabBarController instead of self. Again just suggestion, not sure if it works though.
Using this may help someone: [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:picker animated:NO completion:nil];
Since modalViewController and presentModalViewController are deprecated, the following is what works for me:
presentingVC = [[UIApplication sharedApplication] keyWindow].rootViewController;
if (presentingVC.presentedViewController) {
[presentingVC.presentedViewController presentViewController:VC3 animated:YES completion:nil];
} else {
[presentingVC presentViewController:VC3 animated:YES completion:nil];
}
You can follow this pattern
[VC1 presentModalViewController:VC2];
//
[**VC2** presentModalViewController:VC3];