Change NavigationBar Text from Different ViewController - ios

I have a similar question posted prior to this one, but I now have a more clear question and more information along with code. I currently have a ViewController (SignUpViewController) with a UITextField and a UIButton. I also have another ViewController (ProfileViewController) that has a UINavigationBar. I want to be able to type a Username in the TextField in the SignUpViewController, tap the UIButton, then have the naviBar text in the ProfileViewController become set to the text in the SignUpViewController's TextField. Problem is, I can't access the UITextField from the ProfileViewController. I currently have an NSString in my AppDelegate called "titleString" and am trying to use that as some sort of a solution. Here is my code below if my question has completely thrown you off, as this is sort of difficult to explain over stack overflow:
SignUpViewController:
- (IBAction)submitButton {
ProfileViewController *profileVC = [[ProfileViewController alloc] initWithNibName:nil bundle:nil];
[self presentViewController:profileVC animated:YES completion:nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.titleString = #"Profile";
appDelegate.titleString = usernameTextField.text;
}
- (void)viewDidLoad {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[super viewDidLoad];
}
ProfileViewController:
- (void)viewDidLoad {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.title = appDelegate.titleString;
[super viewDidLoad];
}
it all works fine until I tap the submitButton in the SignUpViewController. What is going on here?

There's several things you could do here to pass data between view controllers.
1) set up a delegate method. The profileViewController would be the delegate of the signInViewController. When the sign in button is pressed, the signInViewController calls the delegate method which the profileViewController is listening for, which passes the title to the profileViewController.
In signInViewController.h:
#protocol SignInDelegate
#required
- (void)didSignInWithTitle:(NSString*)title;
#end
#interface SignInViewController : UIViewController
#property (nonatomic, assign) id<SignInDelegate> delegate;
Then your ProfileViewController would be set as the delegate when you allocate it:
signInViewController.delegate = profileViewController
This is your ProfileViewController.h:
#import "SignInViewController.h"
#interface ProfileViewController : UIViewController <SignInDelegate>
Lastly, make sure your ProfileViewController implements the - (void)didSignInWithTitle:(NSString*)title; method.
2) You could use NSNotificationCenter to post a custom notification with the title attached. This would be useful if you had several other viewControllers that would want to set the title like the profile.
#define UPDATE_NAVBAR_TITLE #"UPDATE_NAVBAR_TITLE"
When the signInViewController is done:
[[NSNotificationCenter defaultCenter] postNotificationName:UPDATE_NAVBER_TITLE object:nil];
Then, make sure you add the ProfileViewController as an observer:
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(navbarUpdated) name:UPDATE_NAVBAR_TITLE object:nil];
For what you're asking I recommend the first one. Good Luck!

Related

iOS delegate Cancel call fails

I've got two view controllers, HomeViewController and AddViewController. HomeViewController is the delegate of AddViewController.
Problem is that when I hit the "Cancel" button in AddViewController, the call back to the delegate seems not to execute. Symptomatically, the Cancel button behaves as if it were not even wired up. Programmatically, breakpoints seem to indicate that control leaves the cancelButton method, yet never reaches addViewControllerDidCancel.
I believe everything is wired properly, and here's the relevant code:
From AddViewController.h:
#import <UIKit/UIKit.h>
#import "WMDGCategory.h"
#import "WMDGActivity.h"
#class HomeViewController;
#protocol AddViewControllerDelegate <NSObject>
-(void) addViewControllerDidSave;
-(void) addViewControllerDidCancel:(WMDGActivity *) activityToDelete;
#end
#interface AddViewController : UIViewController <UIPickerViewDataSource,UIPickerViewDelegate>
#property (nonatomic, weak) id <AddViewControllerDelegate> delegate;
From HomeViewController.h:
#interface HomeViewController : UITableViewController <UITableViewDataSource,UITableViewDelegate,AddViewControllerDelegate>
From HomeViewController.m:
-(void) addViewControllerDidSave
{
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
[localContext MR_saveToPersistentStoreAndWait];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
[self refreshData];
}
-(void) addViewControllerDidCancel:(WMDGActivity *) activityToDelete
{
[activityToDelete MR_deleteEntity];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
[self refreshData];
}
And from AddViewController.m:
- (IBAction)cancelButton:(UIBarButtonItem *)sender
{
[self.delegate addViewControllerDidCancel:self.thisActivity];
}
Can someone spot my mistake?
Thanks!
Edit in response to answer 1 below:
Actually, I did this in my prepareFroSegue method in HomeViewController:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
if ([[segue identifier] isEqualToString:#"addModal"])
{
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
AddViewController *avc = (AddViewController *)navController.topViewController;
avc.delegate = self;
WMDGActivity *addedActivity = (WMDGActivity *)[WMDGActivity MR_createInContext:localContext];
avc.thisActivity = addedActivity;
}
}
Your missing assigning the delegate object of your AddViewController instance, from what I can see it's still nil
myAddViewController.delegate = self; // 'self' is an example, should be myHomeViewController instance
EDIT:
You are calling this in your code:
[self.delegate addViewControllerDidCancel:self.thisActivity];
Ask yourself what is the value of delegate? You have declared it, yes, but you have not set its value, so it must be nil. So when you instantiate your AddViewController you must set the delegate using the line I wrote before.

Sending data between ViewController with JASidePanels

I am trying to implement JASidePanels where CenterViewController slides and reveals LeftViewControler which contains a TableView. Once the user selects a row in the TableView, I'd like the CenterView to regain it's position by sliding back and also have a method (within CenterViewController) do be called with a parameter from LEftViewController to update the CenterView. Can somebody please help my with this?
Thank you.
There are two parts to solving your problem:
first showing center panel when user select a row:
[self.viewController showCenterPanelAnimated:YES];
// add this method to your tableView row
passing a message back to center panel with new instruction, this can be done by creating delegate or notification. to keep it simple i will use Notification:
in your left panel class:
// Add to your tableView row method
NSNotification *msg = [NSNotification notificationWithName:#"leftPanelMsg" object:#"Hello"];
[[NSNotificationCenter defaultCenter] postNotification:msg];
in your center panel class:
add Observer in viewDidLoad and another method when message is passed back:
- (void)viewDidLoad {
[super viewDidLoad];
// method listen to meesssage with specfic name and calls selector when it get hit
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(msgResponder:) name:#"leftPanelMsg" object:nil];
}
-(void)msgResponder:(NSNotification *)notification {
NSLog(#"name:%# object:%#", notification.name, notification.object);
}
If you want to use delegates, then you have to add a protocol to the LeftViewController.h file, e.g.
#protocol LeftViewControllerDelegate <NSObject>
-(void)useThisValue:(NSString *)value;
#end
#interface LeftViewController : UITableViewController
#property (weak, nonatomic) id <LeftViewControllerDelegate> delegate;
#end
In your tableView:didSelectRowAtIndexPath: method, you can then add
[self.delegate useThisValue:menu[indexPath.row]];
Your CenterViewController will become the delegate of the LeftViewController, so in your CenterViewController.h file, import the LeftViewController.h and add:
#interface CenterViewController : UIViewController <LeftViewControllerDelegate>
- (void)useThisValue:(NSString *)value;
In your CenterViewController.m file, import both the AppDelegate.h and your RootViewController.h file. In its viewDidLoad method, you have to get a reference to the current instances of your JASidePanelController subclass (RootViewController) and the leftViewController:
RootViewController *rootViewController = (RootViewController *)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
LeftViewController *leftViewController = (LeftViewController *)rootViewController.leftPanel;
and then make the CenterViewController the delegate of the LeftViewController:
leftViewController.delegate = self;
[super viewDidLoad];
Implement the delegate method however you wish, for example:
- (void) useThisValue:(NSString *)value
{
self.label.text = value;
}
I have to acknowledge and thank Kevin McNeish here for pointing out to me how to properly get the instances of the RootViewController and the LeftViewController in order to get the delegate pattern to work with JASidePanels.
Did you see these methods in JASidePanels
// toggle them opened/closed
- (void)toggleLeftPanel:(id)sender;
- (void)toggleRightPanel:(id)sender;
JASidePanels is a good implementation, you can set the left, center and the right view controllers. So lets say that you have a UITableViewController as the left VC and according to row selection you load the center VC. You should have #import "UIViewController+JASidePanel.h" in your left VC.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row)
case 1:
{
self.sidePanelController.centerPanel = [[UINavigationController alloc] initWithRootViewController:[[FirstViewController alloc] init]];
[self.sidePanelController toggleLeftPanel:nil];
break;
}
case 2:
{
self.sidePanelController.centerPanel = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
[self.sidePanelController toggleLeftPanel:nil];
break;
}
}

Calling a Function in MasterView after dismissing the ModalView in ipad

I am using Master-Detail template for ipad. I have a ViewController, which I want to show modally so I have used this code
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
m_ViewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
m_ViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[appDelegate.splitViewController presentModalViewController:m_ViewController animated:YES];
This works fine and the ViewController is loaded modally, Now I tried to dismiss this ViewController, So inside ViewController.m, I called this line of code
[self dismissModalViewControllerAnimated:YES];
This code also works fine and the ViewController gets dismissed, But after dismissing I want to call a function in my MasterView. How to do that?
Code added according to the discussion with Moxy.
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.testViewController testfunction:testImage];
As amit3117 pointed out, you should use a delegate.
The protocol should be defined at least with a method that would communicate to the delegate that the view controller that was presented modally did finish its work.
#class ViewController;
#protocol MyViewControllerDelegate <NSObject>
-(void)viewControllerDidFinish:(ViewController *)sender;
#end
EDIT : I forgot to add that you should a public property for the delegate to ViewController
#interface ViewController : UIViewController
#property (nonatomic, weak) id <MyViewControllerDelegate> delegate;
#end
You could use your master view controller as the delegate. So in your master view controller implementation you would also have :
#interface MyMasterViewController () <MyViewControllerDelegate>
#end
#implementation MyMasterViewController
-(void)showViewController
{
m_ViewController = [[ViewController alloc] initWithNibName:#"ViewController"
bundle:nil];
m_ViewController.modalPresentationStyle = UIModalPresentationFormSheet;
m_ViewController.delegate = self;
// –presentModalViewController:animated: is deprecated!
[self.parentViewController presentViewController:m_ViewController
animated:YES
completion:nil];
}
-(void)viewControllerDidFinish:(ViewController *)sender
{
// Add any code you want to execute before dismissing the modal view controller
// –dismissModalViewController:animated: is deprecated!
[self.parentViewController dismissViewControllerAnimated:YES
completion:^{
// code you want to execute after dismissing the modal view controller
}];
}
#end
When m_ViewController finishes its work, it should call :
[self.delegate viewControllerDidFinish:self];

how to display UIViewController from a NSObject class?

There is a current view as UIViewController which call "LoginView" but I'm not in, I'm in a NSObject class and I want to call, display an other UIViewController which is call "MapView". How can I do this?
The problem is like above screenshot.
At your IBAction or specific method write this:
UIWindow *window=[UIApplication sharedApplication].keyWindow;
UIViewController *root = [window rootViewController];
UIStoryboard *storyboard = root.storyboard;
CustomViewController *vcc =(CustomViewController *) [storyboard instantiateViewControllerWithIdentifier:#"storyBoardID"];
[root presentModalViewController:vcc animated:YES];
I am assuming you're trying to access your UIViewController member from a UIViewController class from a NSObject class. Easy just pass UIViewController member to the NSObject class. In this case a self. What that lets you do is you can change, edit, remove, whatever you want to do in your UIView from another class. The following is an example of that.
Calling the NSObject Class from your UIViewController class
#implementation myViewControllerClass
- (void) viewDidLoad {
//Pass in the UIViewController object, in this case itself.
[[myNSOBjectClass alloc] startViewController:self];
....
}
Then from your NSObject
#interface myNSOBjectClass{
//Global access to the view controller.
UIViewController *viewController;
}
...
#implementation myNSOBjectClass
...
//function that the caller calls to pass their UIViewController object
- (void)startViewController:(UIViewController *)callerViewController{
viewController = [[UIViewController alloc]init];
//puts the caller's view controller to the global member.
viewController = callerViewController;
...
}
Now you have the view controller at your fingertips!
Cheers :)!
I used like this in my NSObject class:
[[[UIApplication sharedApplication] delegate].window.rootViewController presentViewController:yourMapViewContorller animated:YES completion:nil];
I hope it's useful.
You shouldn't be instantiating and displaying view controllers from within a model. Views should be driven by models.
In this case you mentioned LoginView as your starting point. When some condition is satisfied (successful login perhaps?) you should update the underlying model accordingly, and then display the MapView.
From within LoginView:
MapView *mapView = [[MapView alloc] init];
If your app uses a navigation controller:
[self.navigationController pushViewController:mapView animated:YES];
Otherwise:
[self presentViewController:mapView animated:YES completion:<nil or block>];
Try this Code. It'll help to you......
In your button click action You have to send your UINavigationController & current ViewController. Because NSObject class not found that controller.
In your Button Action put this Code:
[demo login_method_called:self.navigationController withCurrentViewController:self];
In your NSObject .h class put this code:
#import <Foundation/Foundation.h>
#import "Home_ViewController.h"
#interface Method_Action_class : NSObject
- (void)login_method_called:(UINavigationController*)navigation withCurrentViewController:(UIViewController*) controller;
#end
In your NSObject .m class put this code:
#import "Method_Action_class.h"
#implementation Method_Action_class
-(void)login_method_called:(UINavigationController*)navigation withCurrentViewController:(UIViewController*) controller
{
Home_ViewController *home = [[Home_ViewController alloc] initWithNibName:#"Home_ViewController" bundle:nil];
[navigation pushViewController:home animated:YES];
}
#end
And Build your code.
I have created an obersver in view controller class and declare a method of push view controller and post notification using NSNotification centre from NSObject subclass and it is working well.
in view controller:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissPickerView) name:kNotificationDismissPicker object:nil];
in subclass of NSOject:
[[NSNotificationCenter defaultCenter] postNotificationName:kMoveToAchievementView object:nil];

how could two different View Controllers Communicate with each other

I have an application with tab bar and 3 different View Controller. one of these imanage a UItableView that I designed through Interface Builder (storyboard) and I set the it's view controller class in the Inspector -> inspector identity -> and I set class field there, hence, I have no control when this view controller get instantiated, as it's done through storyboard when user click on tab bar. notice, i'm new to objective C and iOS programming.
the issue that I'm facing, i'm also using remote notification. hence, when I receive a remote notification message in "didReceiveRemoteNotification" in the AppDelgate class. I need to update UI interface (above ViewController), but the issue I don't have a reference (pointer) to this ViewController from my AppDelgate class ( or do I?). the problem this ViewController instantiated by storyboard nor programmatically, otherwise I could have kept a reference to it.
I did some reading and I understand I could do this communication via NSNotification, but I think this will be an overkill for a problem that maybe arise just because I'm new to this and I don't have full understanding of iOS development.
Thanks,
NSNotifications are easy to use and are probably the right solution.
In the app delegate that needs to send the message, just put:
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyNotification" object:someObjectYouWantToPassCouldBeAppDelegateOrRemoteNotificationObjectOrAnything];
In the view controller that is receiving the message, put:
-(void)viewDidLoad
{
[super viewDidLoad];
//you can add as many of these as you like to handle different notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"MyNotification" object:nil];
}
-(void)viewDidUnload
{
//make sure you remove every observer you've added here
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MyNotification" object:nil];
[super viewDidUnload];
}
-(void)dealloc
{
//clean up in case viewDidUnload wasn't called (it sometimes isn't)
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
//use a different handler method for each notification
//the method name should match the selector in your observe call in viewDidLoad
-(void)handleNotification:(NSNotification *)notification
{
WhateverClassOfObjectYouWerePassing *object = notification.object;
//now you have a reference to the object that was passed from your app delegate
}
For different methods you want to call, just and a new notification name and a new handler method.
Your app Delegate will have a window property which points to the apps window.
Window Property has a -rootViewController property/method.
For your Tab Based Application it would return you the TabViewController.
Each TabViewController have a method -(NSArray *)viewControllers which returns the ViewControllers inside the Tab. These are arranged in the order.
To Access your applications AppDelegate use [[UIApplication sharedApplication]delegate]
Once you have these viewcontrollers you would know which all viewController these are since you have added it in the XIB files. and can perform your methods
1. Communicate two ViewControllers
If you want to communicate two ViewControllers, you should use #protocol as Apple recommended:
ViewController1.h
#interface ViewController1 : UIViewController<ViewController2Delegate, ViewController2DataSource>
#end
ViewController1.m
- (IBAction)goToViewController2:(id)sender{
if(viewController2 == nil) {
ViewController2 *viewController = [[ViewController2 alloc]
initWithNibName:#"View2" bundle:[NSBundle mainBundle]];
viewController2 = viewController;
}
//...
viewController2.delegate = self;
viewController2.dataSource = self;
[self.navigationController pushViewController:viewController2 animated:YES];
}
- (NSString)viewController:(ViewController2 *)controller itemForSomethingAtIndex:(NSInteger)index{
//Send to viewController2 what it needs
return [items objectAtIndex: index];
}
- (void)viewController:(ViewController2 *)controller didFinishEnteringItem:(NSString *)item{
//Handle the result from the viewController2
NSLog(#"result: %#", item);
}
ViewController2.h
#import <UIKit/UIKit.h>
// Define your delegate methods to return items to the delegate of this viewController
#protocol ViewController2Delegate <NSObject>
- (void)viewController:(ViewController2 *)controller didFinishEnteringItem:(NSString *)item;
#end
// Define your dataSource methods to send items from the dataSource to this viewController
#protocol ViewController2DataSource <NSObject>
- (NSString)viewController:(ViewController2 *)controller itemForSomethingAtIndex:(NSInteger)index;
#end
#interface ViewController2 : UIViewController
#property (nonatomic) id <ViewController2Delegate> delegate;
#property (nonatomic) id <ViewController2DataSource> dataSource;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
#synthesize //...;
- (void)someMethod {
//Get someThing from controller1
NSString *item = [dataSource viewController: self itemForSomethingAtIndex:0];
//Return someThing to controller1
[delegate viewController: self didFinishEnteringItem: item];
}
2. Communicate backgroundTask with viewController
If you want to communicate a background task or handle a push notification, use #NickLockwood's answer. But this dont gonna work if the viewController its not loaded. In this case you should handle that in the AppDelegate:
//Get the appDelegate instance
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//And call your custom method to show what it needs
[appDelegate customMethod];
Your custom method should call controllers consecutevly like:
AppDelegate > RootController > ViewController1 > ViewController2 > myMethod
//do something if viewController2 is visible to the user or push it before do something.
//if you use navigation controller, then you need to ask for the position and className

Resources