I'd like to trigger a message from viewController to another viewController.
And I coded like below, but viewController didn't call delegate.
I want to call delegate without stroyboard. I just want to send a message to another viewController.
viewController2.h
#protocol ViewController2Delegate;
#interface ViewController2 : UIViewController
#property (nonatomic, weak) id<ViewController2Delegate> delegate;
#end
#protocol ViewController2Delegate <NSObject>
- (void)showSomethingByDelegate;
#end
viewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)buttonPressed:(id)sender {
[self.delegate showSomethingByDelegate];
}
#end
viewController.m
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
<ViewController2Delegate>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ViewController2 *vc2 = [[ViewController2 alloc] init];
vc2.delegate = self;
}
// Below method was not called by delegate.
- (void)showSomethingByDelegate {
NSLog(#"Button Was Pressed!!!");
}
#end
well let me show an example more simple.
File: firstVC.h
/* This define the protocol object,
you can write methods required or optional the diference is when
the protocol is used in a class, xcode show you a yellow warning with required methods
like UITableViewDelegate, UITextFieldDelegate... */
#protocol firstVCDelegate <NSObject>
#required
- (void)didMessageFromOtherViewController: (NSString *)messageStr;
#optional
- (void)didOtherMessageNotRequired: (NSString *)messageStr;
#end
/* This is the definition of first UIViewController */
#interface firstViewController : UIViewController
#end
/* This is the definition of second UIViewController object with
a property that is our protocol 'firstVCDelegate' */
#interface secondViewController : UIViewController
#property (nonatomic, weak) id <firstVCDelegate> firstVCDelegate;
- (void)executeDelegateProcess;
#end
File: firstVC.m
#import "firstVC.h"
#pragma mark - First UIViewController with delegate
#interface firstViewController() <firstVCDelegate>
#end
#implementation firstViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Creating the 'secondViewController' object with delegate on this class
secondViewController *svc = [secondViewController new];
// Assign the delegate class
[svc setFirstVCDelegate: self];
// Run the delegate logic
[svc executeDelegateProcess];
}
- (void)didMessageFromOtherViewController:(NSString *)messageStr
{
// Receiving the message from the instance of our protocol in the 'secondViewController' class
NSLog(#"MESSAGE #1: %#", messageStr);
}
- (void)didOtherMessageNotRequired:(NSString *)messageStr
{
// Receiving the message in optional method
NSLog(#"MESSAGE #2: %#", messageStr);
}
#end
#pragma mark - Second UIViewController
#implementation secondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)setFirstVCDelegate:(id<firstVCDelegate>)firstVCDelegate
{
if (firstVCDelegate)
_firstVCDelegate = firstVCDelegate;
}
- (void)executeDelegateProcess
{
// This method is only for demo
// You can execute your delegate in the way you need to use
if (_firstVCDelegate) {
[_firstVCDelegate didMessageFromOtherViewController: #"Hello world, using the required method from secondViewController class"];
[_firstVCDelegate didOtherMessageNotRequired: #"Hello code using the optional method"];
}
}
#end
In your appDelegate.m in the method didFinishLaunchingWithOptions you can put this, and you need to #import "firstVC.h"
self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.autoresizesSubviews = YES;
[self.window makeKeyAndVisible];
[_window setRootViewController: [firstViewController new]];
Execute and see two logs messages, I hope I've helped :)
ViewController2 *vc2 = [[ViewController2 alloc] init];
This line of code is not doing what you think it is, this is creating a NEW instance of ViewController2 and storing it inside the variable vc2. At no point (based on your code) is this variable added to the navigation stack and displayed on screen.
Edit
Also as noticed by Holex, your vc2 variable will be removed from memory after the viewDidLoad is finished as you are not holding onto a reference of it. You would either need to create a property to hold onto it, or push it onto the navigation stack for the navigationController to hold onto it.
There are multiple ways to solve this issue:
Instead of creating a new viewController2, find a reference to the one already displayed on the screen (if it is on the screen).
If its not on the navigation stack, add vc2 to the navigation stack [self.navigationController pushViewController:vc2 animated:YES]; (assuming you have a navigation controller).
If not on the stack, and not intending to use a navigationController, present vc2 modally, such as [self presentViewController:vc2 animated:YES completion:nil];
Related
I have two view controllers used in a Tab Bar Controller: FirstViewController and SecondViewController. I'm trying to call a method in the SecondViewController from the FirstViewController, but do this before the SecondViewController is loaded by selecting the Second Tab. Is this possible? I've tried notifications and delegates and can't seem to get it to work, unless I select the SecondViewController and run ViewDidLoad first, and then call it from the FirstViewController.
This is in objective-c and I'm trying to call setAutoModeTimer() in the SecondViewController.
Here is my code:
FirstViewController.h
#import <UIKit/UIKit.h>
#protocol FirstViewControllerDelegate <NSObject>
- (void) setAutoModeTimer;
#end
#interface FirstViewController : UIViewController
#property (nonatomic,weak) id <FirstViewControllerDelegate> delegate;
#end
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
SecondViewController * myViewController = [[SecondViewController alloc] init];
[myViewController view];
// Do any additional setup after loading the view, typically from a nib.
}
//- (void)loadView{[self.tabBarController.viewControllers makeObjectsPerformSelector:#selector(view)];}
- (IBAction)startTimerButtonPressed:(id)sender {
[self.delegate setAutoModeTimer];
}
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
#end
SecondViewController.m
#import "SecondViewController.h"
#import "FirstViewController.h"
#interface SecondViewController () <FirstViewControllerDelegate>
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
FirstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
firstVC.delegate = self;
}
- (void) setAutoModeTimer
{
NSLog(#"Timer has started");
}
#end
You don't need to keep references in each view controller for another one. What are you doing is creating new instances of UIViewControllers in each view controller.
FirstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
firstVC.delegate = self;
After viewDidLoad will be finished firstVC instance will be released.
Because you created a local var. It is not the same viewController you have in tabBar.
You need to setup connection between view controllers once.
It can be done by setup your UITabBarController programmatically, not in stroryboard.
You can setup viewControllers of UITabBarController once, and setup connections between them.
#import "SecondViewController.h"
#import "FirstViewController.h"
#implementation MainTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *listOfViewControllers = [[NSMutableArray alloc] init];
FirstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
SecondViewController * secondVC = [[SecondViewController alloc] init];
firstVC.delegate = secondVC;
[listOfViewControllers addObject:firstVC];
[listOfViewControllers addObject:secondVC];
[self setViewControllers:listOfViewControllers
animated:YES];
}
I have three UIViewcontroller, namely ViewController, BViewController and CViewController, ViewController is RootViewController from UINavigationController,
I'm pushing from ViewController -> BViewController -> CViewController, through button action.
I have declared protocol in CViewController. This protocol method is working fine for BViewController, but I don't understand how to make work its delegate method in ViewController.
I had created an object of CViewController in View Controller, then declared self for that delegate, though its not working, Below is my code. Please help me. Where I'm doing wrong here !
My Root View Controller, namely ViewController
#import "ViewController.h"
#import "BViewController.h"
#import "CViewController.h"
#interface ViewController ()<testDelegates>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CViewController *cVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CViewController"];
cVC.mydelegate = self;
}
- (IBAction)nextAction:(id)sender {
BViewController *bViewC = [self.storyboard instantiateViewControllerWithIdentifier:#"BViewController"];
[self.navigationController pushViewController:bViewC animated:YES];
}
-(void)TestMethod{
NSLog(#" Its NOT Working");
}
My Second View Controller, Namely BViewController
#import "BViewController.h"
#import "CViewController.h"
#interface BViewController ()<testDelegates>
#end
#implementation BViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)bNextAction:(id)sender {
CViewController *cVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CViewController"];
// cVC.mydelegate = self;
[self.navigationController pushViewController:cVC animated:YES];
}
-(void)TestMethod{
NSLog(#" Its Working If I uncomment to cVC.mydelegate = self");
}
And my third View Controller, Where i Declared a protocol in .h file, namely CViewController
#import <UIKit/UIKit.h>
#protocol testDelegates <NSObject>
-(void)TestMethod;
#end
#interface CViewController : UIViewController
#property (weak) id <testDelegates> mydelegate;
#end
And in .m file
#import "CViewController.h"
#interface CViewController ()
#end
#implementation CViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)cNextAction:(id)sender {
[self.mydelegate TestMethod];
}
You create class and this class destroying after you leave viewDidLoad method.
- (void)viewDidLoad {
[super viewDidLoad];
CViewController *cVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CViewController"];
cVC.mydelegate = self;
}
As I understand you need to perform some methods in the ViewController from the CViewController.
You can pass viewController as delegate from the BViewController and them past it to the CViewController.
Or you can use code like this:
- (IBAction)cNextAction:(id)sender {
id< testDelegates > delegate = self.navigationController.viewControllers[0];
if([delegate conformsToProtocol:#protocol(testDelegates)])
{
[delegate TestMethod];
}
}
This is no best way but it's will be work.
Try this
self.delegate = self.navigationController.viewControllers[0];
You need to declare a another Protocol into BViewController class.
Please follow below steps.
Step 1: BViewController.h
#protocol testDelegates2 <NSObject>
-(void)TestMethod2;
#interface BViewController : UIViewController
#property (weak) id <testDelegates> mydelegate;
#end
#implementation CViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)cNextAction:(id)sender {
[self.mydelegate TestMethod2];
}
Step 2. In ViewController class conform Protocol
#interface ViewController ()<testDelegates>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CViewController *cVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CViewController"];
cVC.mydelegate = self;
}
- (IBAction)nextAction:(id)sender {
BViewController *bViewC = [self.storyboard instantiateViewControllerWithIdentifier:#"BViewController"];
cVC.mydelegate = self; //important line
[self.navigationController pushViewController:bViewC animated:YES];
}
-(void)TestMethod2{
NSLog(#"Final Output");
}
If Your are trying to set Delegate of CViewController to ViewController then
try putting
self.mydelegate = self.navigationController.viewControllers[0]
on viewDidLoad method of CViewController.
Else you are Trying to make ViewController And BViewController both be Delegate of CViewController then this is not possible like this, as Delegate of a Object Can only be unique. This means the delegate is simply a variable which holds reference of object and as you know value to a variable can be unique at a instance of time.
So If you are willing to do some task on both Viewcontroller and BViewController then use notification pattern. See apple doc here
Delegate declaration
// FolderListViewController.h
#import <UIKit/UIKit.h>
#import "Folder.h"
#protocol FolderSelectionDelegate <NSObject>
#required
- (void)setFolder:(Folder *)folder;
#end
#interface FolderListViewController : UITableViewController
#property (nonatomic, assign) id<FolderSelectionDelegate> delegate;
- (IBAction)showDashboard:(id)sender;
#end
Delegate is called from didSelectRowAtIndexPath:
// FolderListViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.delegate setFolder:dataSource[indexPath.row]];
}
The VC that should be receiving the message
#import <UIKit/UIKit.h>
#import "FolderListViewController.h"
#import "Folder.h"
#interface ProjectListViewController : UITableViewController <FolderSelectionDelegate, UISplitViewControllerDelegate, UISearchBarDelegate, UINavigationControllerDelegate>
#property (nonatomic,retain)UIActivityIndicatorView *activityIndicatorObject;
#property (nonatomic, copy)Folder *folder;
-(void)loadProjects:(Folder*)folder;
#end
Action that presents the VC
- (void)foldersButtonTapped {
UINavigationController *vc = [[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"FolderListNavController"];
vc.delegate = self;
[self presentViewController:vc animated:YES completion:nil];
}
Delegate method implementation
- (void)setFolder:(Folder *)folder {
_folder = folder;
[self loadProjects:folder];
}
I have read through multiple threads on here and haven't had any luck. At first, I didn't have the reference to ProjectListVC setup when presenting the FolderListVC (ie, vc.delegate = self). That doesn't seem to be the problem here though. I am working on an app that was built for iPad and scaling it to work across all devices. The implementation as it is here works (it's setup as a split view controller). Any help would be greatly appreciated
I think that UINavigationcontroller is creating problem as it is not setting the FolderListViewController delegate . Can You try like this.
FolderListViewController *vc=[self.storyboard instantiateViewControllerWithIdentifier:#"FolderListViewController"];
vc.delegate=self; // protocol listener
[self.navigationController pushViewController:vc animated:YES];
I'm fairly new to Objective-C, programming. The one thing, I'm currently putting up with is passing a value from the first ViewController to the next one.
I've read this entry here and it didn't help me. Which is funny, since his answer has nearly 500 votes, so I must be the problem. What I did was the following.
I opened my PreviewViewController.m and added the following line #import "MainViewController.h" since I wanted to pass a value from the PreviewViewController to the `MainViewController. Then, when I switch the layouts ( which successfully works ) I want to pass a value.
MainViewController *mainViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
mainViewController.userId = #"539897197";
As you can see, I want to pass the userId. For that, I also created a property in the MainViewController.h
#property ( nonatomic, strong ) NSString *userId;
Now, In my MainViewController.m I want to access the userId. But when I log it, the console tells me it is null. However, when I set the variable right before the NSLog it works, so it seems like the passing is the problem.
Additionally in the MainViewController.m I have the following line
#synthesize userId = _userId;
but even when I removed that line and changed the NSLog to NSLog(#"%#",self.userId); the same problem occurred.
How can I successfully pass the variables? Which step am I doing wrong?
EDIT
This is how I switch the layouts
UIViewController *viewController = [[MainViewController alloc]init];
MainViewController *mainViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
mainViewController.userId = #"539897197";
[self presentViewController:viewController animated:YES completion:NULL];
Why not create a custom initializer and pass it in that way? Something like
- (id)initWithUserId:(NSString *)aUserId {
self = [super initWithNibName:#"MainViewController" bundle:nil];
if (self) {
self.userId = aUserId
}
return self;
}
Then you can just do:
MainViewController *mvc = [[MainViewController alloc] initWithUserId:#"1234"]
[self presentViewController:mvc animated:YES completion:nil];
If you are using storyboard then you should use prepareForSegue: method to pass data between view controllers. First Create the segue from your PreviewViewController to MainViewController, just control drag from your view controller to next viewcontroller to create segue. Use UINavigationController if you are using push segue. Use this method to segue and pass data
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"yourSegueIndentifier"]){
MainViewController *mvc = (MainViewController *) segue.destinationViewController;
mvc.userId = #"539897197";;
}
}
UIViewController *viewController = [[MainViewController alloc]init];
MainViewController *mainViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
mainViewController.userId = #"539897197";
[self presentViewController:mainViewController animated:YES completion:NULL];
use mainViewController rather than viewController when presenting the view controller
Here we have two ways for passing data.
First is Custom Delegate
Second is NSNotification
Here we have two view controllers.
ViewController
and
SecondViewController
in SecondViewController
.h
#import <UIKit/UIKit.h>
#class SecondViewController;
#protocol SecondViewControllerDelegate <NSObject>
- (void)secondViewController:(SecondViewController *)secondViewController didEnterText:(NSString *)text;
#end
#interface SecondViewController : UIViewController
#property (nonatomic, assign)id<SecondViewControllerDelegate> delegate;
#property (nonatomic, strong) IBOutlet UITextField *nameTextField;//It must connect as outlet connection
- (IBAction)doneButtonTapped:(id)sender;
#end
.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
//Either use NSNotification or Delegate
- (IBAction)doneButtonTapped:(id)sender;
{
//Use Notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"passingDataFromSecondViewToFirstView" object:self.nameTextField.text];
//OR Custom Delegate
[self.delegate secondViewController:self didEnterText:self.nameTextField.text];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
in ViewController
.h
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface ViewController : UIViewController<SecondViewControllerDelegate>
#property (nonatomic, strong) IBOutlet UILabel *labelName; //You must connect the label with outlet connection
- (IBAction)gotoNextView:(id)sender;
#end
.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//addObserver here...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFromPreviousViewControllerNotificationReceived:) name:#"passingDataFromSecondViewToFirstView" object:nil];
// Do any additional setup after loading the view, typically from a nib.
}
//addObserver Method here....
- (void)textFromPreviousViewControllerNotificationReceived:(NSNotification *)notification
{
// set text to label...
NSString *string = [notification object];
self.labelName.text = string;
}
- (IBAction)gotoNextView:(id)sender;
{
//If you use storyboard
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
//OR If you use XIB
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondViewController.delegate = self;
[self.navigationController pushViewController:secondViewController animated:YES];
}
//Calling custom delegate method
- (void)secondViewController:(SecondViewController *)secondViewController didEnterText:(NSString *)text
{
self.labelName.text = text; //Getting the data and assign the data to label here.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
For your understanding the code I create a simple passing data from one second view controller to first view controller.
First we navigate the view from first view controller to second view controller.
After that we send the data from second view controller to first view controller.
NOTE : You can either use NSNotification or Custom Delegate method for sending data from One View Controller to Other View Controller
If you use NSNotification, you need to set the postNotificationName for getting data in button action method.
Next you need to write addObserver in (sending data to your required View Controller) ViewController and call the addObserver method in same View Controller.
If you use custom delegate,
Usually we go with Custom Protocol Delegate and also we need to Assign the delegate here.
Very importantly we have to set the Custom Delegate Method in the Second View Controller.Because where we send the data to first view controller once we click the done button in second view controller.
Finally we must call the Custom Delegate Method in First View Controller, where we get the data and assign that data to label.Now you can see the passed data using custom delegate.
Likewise you can send the data to other view controller using Custom Delegate Methods
I tried and got the solution for passing the value between viewcontroller.
MainViewController *mainVC = [[self storyboard] instantiateViewControllerWithIdentifier:#"MainViewController"];
mainVC.userId = #"539897197";
[self presentViewController:mainVC animated:YES completion:Nil];
or using this way . You don`t use the storyboard use the following its working fine
MainViewController *mainVC = [[MainViewController alloc] init];
mainVC.userId = #"539897197";
[self presentViewController:mainVC animated:YES completion:Nil];
In a view, let's call it firstView I created a secondView as follows and pushed it if certain thing happened in the firstView:
SecondViewController *secondVC = [[secondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:secondVC animated:YES];
[secondVC release];
Now when I'm in the secondView if let say a button is pressed I want to go back to firstView and also pass back a value from secondView to the firstView (let say an integer value of a textfield from secondView to the firstView).
Here is what I tried:
#protocol SecondViewControllerDelegate;
#import <UIKit/UIKit.h>
#import "firstViewController.h"
#interface SecondViewController : UIViewController <UITextFieldDelegate>
{
UITextField *xInput;
id <SecondViewControllerDelegate> delegate;
}
- (IBAction)useXPressed:(UIButton *)sender;
#property (assign) id <SecondViewControllerDelegate> delegate;
#property (retain) IBOutlet UITextField *xInput;
#end
#protocol SecondViewControllerDelegate
- (void)secondViewController:(SecondViewController *)sender xValue:(int)value;
#end
And in the m file
- (IBAction)useXPressed:(UIButton *)sender
{
[self.delegate secondViewController:self xValue:1234]; // 1234 is just for test
}
And then in the firstView I did:
#import "SecondViewController.h"
#interface FirstViewController : UITableViewController <SecondViewControllerDelegate> {
}
#end
And implemented:
- (void) secondViewController:(SecondViewController *)sender xValue:(int)value
{
[self.navigationController popViewControllerAnimated:YES];
}
Now, the problem is for one in FirstViewController I get the warning that "No definition of protocol "SecondViewControllerDelegate" is found, and for two the delegate method (last piece of code above) does not get invoked at all. Can somebody please tell me what's wrong?
After this line
SecondViewController *secondVC = [[secondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
Add
secondVC.delegate = self;
Also instead of
- (void) secondViewController:(SecondViewController *)sender xValue:(int)value
{
[self.navigationController popViewControllerAnimated:YES];
}
You should use
- (void) secondViewController:(SecondViewController *)sender xValue:(int)value
{
[sender popViewControllerAnimated:YES];
}
In FirstViewController .h file :
#import "SecondViewController.h"
#interface FirstViewController : UITableViewController <SecondViewControllerDelegate> {
SecondViewController *secondViewController;
}
#end
In implementation file , where you init SecondViewController instance next line assign self to delegate property :
secondViewController.delegate = self;
Next define delegate method :
- (void)secondViewController:(SecondViewController *)sender xValue:(int)value
{
NSLog ("This is a Second View Controller with value %i",value)
}
For problem 1: The #protocol definition for SecondViewControllerDelegate looks like it's in secondViewController.h; are you sure this file is imported in firstViewController.h? Otherwise it won't know about the protocol.
Problem 2: It might be totally unrelated to problem 1. Are you sure the action is hooked up properly? Can you put a NSLog() call in your useXPressed: to make sure that method is actually getting called when you expect it to?