I have created a Tab Bar Application using Xcode that as two views.
The secound view is a UITableViewController.
What I am struggling to do is send data to this view, when the second tab is pressed. I have delegated the Tab Bar to my AppDelegate class and implemented this function:
-(void)tabBarController:(UITabBarController*)tabBarController didSelectViewController:(UIViewController*)viewController
{
// Override point for customization after application launch.
statisticsViewController* assignmentListcont = [statisticsViewController alloc];
NSManagedObjectContext* context = [self managedObjectContext];
assignmentListcont.managedObjectContext = context;
[assignmentListcont release];
}
The second view is displaying fine but the data hasn't been passed. I imagine its because I haven't programmed the second views transition but I'm unsure of how to do this if I already have a .xib file doing it for me? Is there some way to just pass the data without problems or even retrieve the data once inside the view?
You could use notifications.
In the view that you want to receive the data, put this in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(yourSelector:)
name:#"addedData"
object:nil];
Then implement the method that receives that data:
- (void)yourSelector:(NSNotification *)notification {
Foo *foo;
foo = [notification object];
//do something else
}
Now in the class where the data is originated from, you post a notification that new data was created. Also, you pass along the data that you want to have the other method receive.
[[NSNotificationCenter defaultCenter] postNotificationName:#"addedData"
object:foo];
I've done this to send a value from one view controller to another, hope it helps
UITabBarViewController *var = [self.storyboard instantiateViewControllerWithIdentifier:#"name"]; //I select the UITabBarController
otherViewController *var2 = [var.childViewControllers objectAtIndex:0]; //I Select the first ViewController from that UITabBarController
var2.variable = #"value";
[self.navigationController pushViewController:var animated:YES];
Related
My issue for my function is I am not sure how to pass the data which i selected, and back to the previous view controller. The link is to the whole paragraph,http://pastebin.com/AtMjLD66 (sorry I don't have 10 reputations, sorry about the inconvenience ) Cheer.
I would recommend delegates for this but if you don't want to do so, NSNotification would save your day here.
Add this is in advanceVC.m probably in viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(someThingSelectedInDetail:) name:#"someThingSelectedInDetail" object:nil];
- (void)someThingSelectedInDetail:(NSNotification*)notificationObject
{
NSString *chefName = notificationObject.object;
NSLog(#"Chef Name from notification: %#",chefName);
}
And in didSelect of advanceDetailVC.m do this.
UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
[[NSNotificationCenter defaultCenter ]postNotificationName:#"someThingSelectedInDetail" object: cell.textLabel.text];
Instead of passing a label's text you have the indexpath here so take it directly from the array and pass it in the notification like this
[[NSNotificationCenter defaultCenter ]postNotificationName:#"someThingSelectedInDetail" object: [detailArray objectAtIndex:indexPath.row]];
You would use the delegate pattern to send a message back to the view controller's delegate (which you could set to the presenting view controller), passing along whatever information you need to about the selection.
Assuming you want it for iOS, a simple call :
[[[UIApplication sharedApplication] delegate] doSomething];
or
Since you only have one view controller, the generic way (independent of how your app was set up):
UIViewController *vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
You can use nsuserdefault to save data and then you can retrieve back in the previous class
I have an app where there are two basic functions, delineated by being on separate tabs on a UITabBarController. Each tab has a particular button that when pressed should, as a separate function to its normal activity, also asynchronously tell the other tab to nil its data. How can I access one tab from the other?
This would be a good use for notifications (as in NSNotification, not local or push notifications).
You should have some sort of model for each view controller in each tab. Each model can publish its notification while registering for the other. This way, neither view controller or model needs to actually know about the other directly.
When the user taps a button, the view controller tells its model to publish its notification. The other one will get the notification and act accordingly.
See the docs for NSNotificationCenter and NSNotification for details.
Definition of "tabs" for UITabBarController
// define controllers for each tab
UIViewController *viewController1 = [[UIViewController alloc] init];
UIViewController *viewController2 = [[UIViewController alloc] init];
// define tab bar controller, "self" is a UITabBarController
self.viewControllers = [NSArray arrayWithObjects: viewController1, viewController2, nil];
From this point, if you need to access a particular tab, you want to do so via the "viewController1" or "viewController2" objects. Each of these UIViewController objects would presumably have access to certain data in your application.
You can access each of your viewControllers from TabBarController's viewControllers property, and iterate through them. Zero all but the live one (self).
Put it into a GCD dispatch queue for asynchronicity.
- (IBAction)pushButton:(id)sender {
NSLog (#"%# %#",self,NSStringFromSelector(_cmd));
//do normal stuff here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (id viewController in [[self tabBarController] viewControllers]) {
if ((viewController != self)
&& ([viewController respondsToSelector:#selector(zeroData)])) {
[viewController performSelector:#selector(zeroData)];
}
}
});
}
- (void) zeroData
{
NSLog (#"%# %#",self,NSStringFromSelector(_cmd));
//each view controller should zero out it's own data
//in a method called "zeroData"
}
If you try this and look at the logs, you will see that it leaves the current vc alone but sends zeroData to the others...
Are your tabs core-data driven? If you use a NSFetchedResultsController then you get notifications for free through the NSFetchedResultsControllerDelegate protocol. Otherwise, NSNotification like rmaddy suggests.
I currently have written a slider (similar to the Facebook app) for my app. At the top of the slider is a Search Box, and the methods controlling the search functionality are also within the app delegate.
Similarly, I have the methods that control the slider's table view in a separate class (SliderMenuViewController).
I am looking for a way for the slider (either the search box or the tableview cells) to be able to tell the RootViewController (or whichever viewController is currently visible) to push a new ViewController (inside a UINavigationController).
This is what I tried to do (this code is in the AppDelegate):
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
NSLog(#"Searching for: \"%#\"",searchBar.text);
[searchBar resignFirstResponder];
IndexAndSearch *vc = [[IndexAndSearch alloc]initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
}
But it doesn't work (it writes to the log, but doesn't push the new ViewController). I also tried sending a message to the RootViewController like this:
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
NSLog(#"Searching for: \"%#\"",searchBar.text);
[searchBar resignFirstResponder];
RootViewController *vc = [[RootViewController alloc]initWithNibName:nil bundle:nil];
[vc performSearchFromDelegateSlider];
}
With the following code in the RootViewController's implementation file:
-(void)performSearchFromDelegateSlider{
NSLog(#"Searching");
IndexAndSearch *vc = [[IndexAndSearch alloc]initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
}
But once again it only wrote to the log, not pushing a viewController.
I've looked far and wide on Google and SO, but haven't been able to find anything useful. This question is similar to mine, but there haven't been any suitable answers. I know the answer probably involves delegation, but I can't wrap my head around a solution for this.
Important note: This slider is available from nearly every ViewController in the app, meaning that whatever solution I implement has to be able to push a new ViewController for every class. That is why I can't use a solution like this one (I would have to enter the NavigationDelegate code into each ViewController, which won't work in an app as large as mine).
Thanks in advance for your help guys.
I'm not convinced it is the best solution, but I was able to get this working using notifications. For anyone that is interested, here is what I did:
Step 1
The first step is to register for the notification in the RootViewController's viewDidLoad method:
-(void)viewDidLoad{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveNavSliderSearchNotification:) name:#"navSliderSearchNotification" object:nil];
}
Step 2
I then need to fire the notification when the search is performed from the slider. The searchBar code is located in my AppDelegate and looks like so:
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
//Write to the log
NSLog(#"Searching for: \"%#\"",searchBar.text);
//Dismiss the keyboard
[searchBar resignFirstResponder];
//Post the notification (to be used by the RootViewController
[[NSNotificationCenter defaultCenter] postNotificationName:#"navSliderSearchNotification" object:self];
}
Step 3
I then need to write the didReceiveNavSliderSearchNotification class (which will be called in the RootViewController when the navSliderSearchNotification notification is posted and received):
-(void)didReceiveNavSliderSearchNotification:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"navSliderSearchNotification"])
NSLog (#"Successfully received the search notification!");
//Push the next ViewController when the *navSliderSearchNotification* is received
IndexAndSearch *vc = [[IndexAndSearch alloc]initWithNibName:#"IndexAndSearch" bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
}
And that is how I managed to push new viewControllers from a separate class (in this case the App Delegate, but I also have it working from other classes as well).
Final step (optional)
My slider is accessible from everywhere in the app, so I did not unregister from my notifications in the RootViewController (meaning these methods will continue to fire even if the user has been pushed to another viewController). If you do not want this functionality, make sure to unregister from these notifications using the following code (it would go in the RootViewController):
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"navSliderSearchNotification" object:nil];
}
Like I said, I am not entirely convinced this is the best method. If you have another solution that you prefer, please feel free to post it.
Have you tried doing this:
Create a UIViewController variable in your AppDelegate that always references the current UIViewController on the screen (You might need to set the current controller every time you create a view controller)
Once that's all done.
In your AppDelegate use
[self.currentViewController.navigationController pushViewController:anotherNewViewController animated:YES];
I have an app that has a navigation controller in a tabbar controller. The root for the navigation controller is a table view controller. The table view has a segue to an image view. I want to update the badge value on a tab from the image view controller. From the tabbar controller, this works fine:
UIViewController *viewController = [self.tabBarController.viewControllers objectAtIndex:1];
viewController.tabBarItem.badgeValue = #"x";
But when I put the same code into the imageview controller, it doesn't work. When I check the value of 'viewController' after it executes, the value is nil. Same for self.tabBarController. For some reason the image view controller can't see its tabbarcontroller.
You should be able to access the tabBarController with self.navigationController.tabBarController. So your code needs to be:
UIViewController *viewController = [self.navigationController.tabBarController.viewControllers objectAtIndex:1];
viewController.tabBarItem.badgeValue = #"x";
so there are many ways to do this but the best way would be subcscribe to an NSNotification and then from your image view or wherever, make that notification so it would look something like.
[[NSNotificationCenter defaultCenter]
addObserver:objectToListen
selector:#selector(methodToRun:)
name:#"NotificationName"
object:nil];
And then in you deeper items such as the imageview or whatever you post it like this
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationName" object:nil userInfo:dictionaryWithValuesForMethodToUse to use];
And then you get the information from the userInfo like so in you method that changes the badge value
- (void)methodToRun:(NSNotification *)notification
{
NSDictionary *dictionary = [notification userInfo];
}
I have a viewController I've built in storyboard. I also have a NSObject Subclass which acts as my model, which sends and listens for API requests and responses. When a method fires in my model, I want to present a modal View of my viewController from whatever view happens to be visible at the time.
An example would be if my API hears "show this view" I want to show viewController regardless of what view is being shown.
Conceptually, how does one do this?
EDIT: I don't know which view controller will be showing when I want to present my modal viewController. Also, I need to pass params from my model to the modalVC when it's presented.
I would send a notification from the model telling "someone" that some view needs be displayed.
NSDictionary *userInfo = #{ #"TheViewKey": viewToDisplay];
[[NSNoticationCenter defaultCenter] postNotificationName:#"NotificationThatThisViewNeedsToBeDisplayed" object:self userInfo:userInfo];
And then on the delegate (or the active view controller) would register to this notification and handle the display.
// self is the delegate and/or the view controller that will receive the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleViewToDisplay:) name:#"NotificationThatThisViewNeedsToBeDisplayed" object:nil];
If you put in the view controller remember to remove self from the observers when the view is not visible:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"NotificationThatThisViewNeedsToBeDisplayed"];
This way your model is decoupled from the presentation.
You have the current viewController (any viewController subclass) present the new view using:
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
EDIT: To find the top view controller, you ask the UITabBarController for the selectedViewController (if you use a tabBarController) to get the 'seed', or start with the window.rootViewController.
Once you are past any tabBarControllers, then you should only have UIViewController subclasses and UINavigationControllers. You can use a loop like this:
- (UIViewController *)frontmostController:(UIViewController *)seed
{
UIViewController *ret;
if([seed isKindOfClass:[UINavigationController class]]) {
ret = [(UINavigationController *)seed topViewController];
} else
if([seed isKindOfClass:[UIViewController class]]) {
ret = seed.presentedViewController;
}
return ret ? [self frontmostController:ret] : seed;
}