I have a little trouble with my delegate example. I created a very simple code to learn how delegates work. I know that my delegate not will be called but i can't figure out why?
So here is the complete code. Please tell me what i do wrong. It is really important for me to understand the error in this code.
First Viewcontroller: h.file
#import <UIKit/UIKit.h>
#protocol ViewControllerDelegate;
#interface ViewController : UIViewController
#property (nonatomic, retain) id<ViewControllerDelegate> delegate;
#end
#protocol ViewControllerDelegate <NSObject>
- (void)transfer:(ViewController *)data number:(NSUInteger)value;
#end
First Viewcontroller: m.file
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
if ([delegate respondsToSelector:#selector(transfer:number:)]){
[delegate transfer:self number:65];
NSLog(#"delegate called");
}
[delegate transfer:self number:65]; //Try to call without if-statement.
}
#end
SecondViewcontroller: h.file
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface SecondViewController : UIViewController <ViewControllerDelegate>
#end
SecondViewcontroller: m.file
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)transfer:(ViewController *)data number:(NSUInteger)value
{
NSLog(#"received information from ViewController");
}
- (void)viewDidLoad
{
[super viewDidLoad];
ViewController *viewcontroller = [[ViewController alloc] init];
viewcontroller.delegate = self;
}
#end
In the storyboard i use two container views so both ViewControllers will shown.
Your current code is fine. The problem appears to be that you are never loading the view controllers view(s) so the viewDidLoad method isn't being called.
To test, push the viewcontroller, or just request viewcontroller.view.
Related
I have read around, and it seems as though delegates would be really useful in my app. Unfortunately, every tutorial about protocols I have tried has failed - the delegate is not receiving the message! It would be great if someone could tell me what I'm doing wrong.
I created a really simple test app with two ViewControllers, a FirstViewController and a SecondViewController. I have set them up in container views to see the effect properly.
My Main.storyboard looks like this:
The purpose of the test app is to change the background colour of the SecondViewController when one of the buttons is pressed in the FirstViewController.
Here is FirstViewController.h:
#import <UIKit/UIKit.h>
#protocol FirstViewControllerDelegate
-(void)colourDidChange:(UIColor *)theColour;
#end
#interface FirstViewController : UIViewController{
UIButton *redButton;
UIButton *blueButton;
}
#property (nonatomic, retain) id <FirstViewControllerDelegate> delegate;
#property (nonatomic, retain) IBOutlet UIButton *redButton;
#property (nonatomic, retain) IBOutlet UIButton *blueButton;
-(IBAction)redPressed;
-(IBAction)bluePressed;
My FirstViewController.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize redButton, blueButton;
#synthesize delegate;
-(IBAction)redPressed{
[self.delegate colourDidChange:[UIColor redColor]];
}
-(IBAction)bluePressed{
[self.delegate colourDidChange:[UIColor blueColor]];
}
-(void)viewDidLoad
{
[super viewDidLoad];
}
I think I have implemented the protocol and the calling of the delegate correctly.
Here is my SecondViewController.h:
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface SecondViewController : UIViewController <FirstViewControllerDelegate>
-(void)colourDidChange:(UIColor *)theColour;
And my SecondViewController.m:
-(void)colourDidChange:(UIColor *)theColour{
self.view.backgroundColor = theColour;
}
-(void)viewDidLoad
{
[super viewDidLoad];
FirstViewController *firstView = [[FirstViewController alloc]init];
firstView.delegate = self;
}
I have breakpointed the project and realised that colourDidChange: in the SecondViewController is never executed.
It would be much appreciated if someone could point out what I have done wrong, whether declaring (or conforming to) the delegate poorly or not setting the delegate the right way.
Many thanks.
I suspect that there are 2 instances of FirstViewController, one created by your storyboard and another one created in SecondViewController's viewDidLoad method.
When theFirstViewController creates SecondViewController it could set the delegate property or use an Outlet to connect them.
Note: delegate properties should not be retain, they should be assign (or weak with ARC).
You are honestly very close. Container views will call the prepareForSegue: method, so you should be initializing the second view controller's delegate in this method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
This way you know that you are getting the instance of SecondViewController that will be in use. Also, you do not need to redeclare the delegate method in your SecondViewController.h file:
-(void)colourDidChange:(UIColor *)theColour;
Finally, in storyboard set the title of the container view segue to SecondViewController to whatever title you like and then copy paste that title to where 'TypeContainerViewSegueNameHere' is written above.
EDIT 1:
A typical situation would be similar to this:
#protocol ViewControllerDelegate;
#interface ViewController : UIViewController
#property (nonatomic, strong) id<ViewControllerDelegate>delegate;
#end
#protocol ViewControllerDelegate <NSObject>
- (void) delegateMethod;
#end
...
#implementation ViewController
- (void) buttonAction:(id)sender {
[self.delegate delegateMethod];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
#end
...
#interface SecondViewController : UIViewController <ViewControllerDelegate>
#end
...
#implementation SecondViewController
- (void)delegateMethod {
}
#end
That said, you could make your main view controller the delegate of your FirstViewController, which has the two view containers as seen in your screenschot. And then call a delegate method from the main view controller to the second view controller. Although I am curious as to why you have these two view controllers as child view controllers rather than placing a view and two buttons in one view controller.
EDIT 2:
Here is an example (written quickly and not tested). Think of it as a triangle of delegates:
#protocol FirstViewControllerDelegate;
#interface FirstViewController : UIViewController
#property (nonatomic, strong) id<FirstViewControllerDelegate>delegate;
#end
#protocol FirstViewControllerDelegate <NSObject>
- (void) firstViewControllerDelegateMethod;
#end
...
#implementation FirstViewController
- (void) buttonAction:(id)sender {
[self.delegate firstViewControllerDelegateMethod];
}
#end
...
#protocol MainViewControllerDelegate;
#interface MainViewController : UIViewController <FirstViewControllerDelegate>
#end
#protocol MainViewControllerDelegate <NSObject>
- (void) mainViewControllerDelegateMethod;
#end
...
#implementation MainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self.delegate;
}
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
FirstViewController *viewController = (FirstViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
- (void)firstViewControllerDelegateMethod {
[self.delegate mainViewControllerDelegateMethod];
}
#end
...
#interface SecondViewController : UIViewController <MainViewControllerDelegate>
#end
...
#implementation SecondViewController
- (void)mainViewControllerDelegateMethod {
}
#end
Like I said, you should think about reducing the complexity of this section of your app and consider putting all of your views in one view controller.
I'm studing protocol and delegates using an Example! When I try to re create this example I notice that this condition is not respected:
if([delegate respondsToSelector:#selector(amountEntered:)]) {blabla}
where is the mistake? Scripts:
First View .h
#import <UIKit/UIKit.h>
#import "EnterAmountViewController.h"
#interface DelegateExampleViewController : UIViewController <EnterAmountDelegate>{
IBOutlet UILabel *amountLabel;
}
-(IBAction)changeAmountPressed;
#end
First View .m
#import "DelegateExampleViewController.h"
#implementation DelegateExampleViewController
-(IBAction)changeAmountPressed
{
EnterAmountViewController * enterAmountVC = [[EnterAmountViewController alloc]init];
enterAmountVC.delegate = self;
}
-(void)amountEntered:(NSInteger)amount
{
amountLabel.text = [NSString stringWithFormat:#"%i" , amount];
}
#end
Second View .h
#import <UIKit/UIKit.h>
#protocol EnterAmountDelegate <NSObject>
-(void)amountEntered:(NSInteger)amount;
#end
#interface EnterAmountViewController : UIViewController {
IBOutlet UITextField *amountTextField;
id<EnterAmountDelegate> delegate;
}
-(IBAction)savePressed;
#property(nonatomic,retain) id<EnterAmountDelegate> delegate;
#end
Second View .m
#import "EnterAmountViewController.h"
#import "DelegateExampleViewController.h"
#implementation EnterAmountViewController
#synthesize delegate;
- (void)viewDidLoad {
[super viewDidLoad];
amountTextField.text = #"";
[amountTextField becomeFirstResponder];
}
-(IBAction)savePressed
{
if([delegate respondsToSelector:#selector(amountEntered:)])
{
[delegate amountEntered:[amountTextField.text intValue]];
NSLog(#"rugg");
}
}
#end
Thanks in advance!
In the method:
-(IBAction)changeAmountPressed
{
EnterAmountViewController * enterAmountVC = [[EnterAmountViewController alloc]init];
enterAmountVC.delegate = self;
}
you are creating an instance of EnterAmountViewController on the stack as a local variable. This variable will be inaccessible at the end of the scope. So, when you execute savePressed you are doing it on a different object where you did not set delegate.
In other words, when you check
if([delegate respondsToSelector:#selector(amountEntered:)])
it returns NO because delegate is nil...
The correct way to set the delegate is using the prepareForSegue mechanism:
#implementation DelegateExampleViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[(EnterAmountViewController*)segue.destinationViewController setDelegate:self];
}
...
You do not need the changeAmountPressed method nor the relative binding.
I'm trying to call a method from another class with a simple button in my storyboard.
Here are my files:
ViewController.m
// ViewController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "PrintHello.h"
#interface ViewController : UIViewController <NSObject>{
PrintHello *printMessage;
}
#property (nonatomic, retain) PrintHello *printMessage;
#end
ViewController.m
// ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize printMessage;
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(#"ViewDidLoad loaded");
}
- (IBAction)Button01:(id)sender{
self.printMessage = [[PrintHello alloc] init]; // EDIT: THIS LINE WAS MISSING NOW IT WORKS
[self.printMessage Print];
NSLog(#"Button01 Pressed");
}
#end
PrintHello.h
// PrintHello.h
#import <Foundation/Foundation.h>
#interface PrintHello : NSObject
-(void) Print;
#end
PrintHello.m
// PrintHello.m
#import "PrintHello.h"
#implementation PrintHello
-(void)Print{ NSLog(#"Printed");}
#end
And also in the storyBoard there is a Button01 linked to the Viecontroller.
From the Log i know that:
viewDidLoad is loaded
and the button is pressed when is pressed :)
BUT the method Print is not called?
Where am i doing wrong?
Before you call [self.printMessage Print];, I think you need to put self.printMessage = [[PrintHello alloc] init];.
As woz has said, you haven't initialized printMessage yet, so the object doesn't exist yet! You probably want to initialize it within viewDidLoad of your ViewController.m file, rather than reinitialize the object over and over again within the button click.
-(void)viewDidLoad
{
[super viewDidLoad];
self.printMessage = [[PrintHello alloc] init];
NSLog(#"ViewDidLoad loaded");
}
I know this is just a fundamental question but still somewhere I am missing something, I am playing with passing data to a textView from another class. For this I have created two classes one with xib file (ViewController) and another without(secondVC).
What I am trying to do is that I have a textview in ViewController class and wanted to pass the data to this textView from secondVC. This is how I am doing.
//ViewController.h
#import <UIKit/UIKit.h>
#import "secondVC.h"
#interface ViewController : UIViewController{
IBOutlet UITextView *textView;
}
#property (nonatomic, retain) UITextView *textView;
- (IBAction)go:(id)sender;
#end
//ViewController.m
- (IBAction)go:(id)sender{
secondVC *sec = [[secondVC alloc] init];
[sec print];
}
//secondVC.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface secondVC : UIViewController
- (void)print;
#end
//secondVC.m
- (void)print{
NSString *printThis = #"This works";
ViewController *vc = [[ViewController alloc] init];
[vc.textView setText:printThis];
//vc.textView.text = printThis //Tried both
}
Any suggestions would be appreciated.
Thanks
you can do like this :
//ViewController.h
#import <UIKit/UIKit.h>
#import "secondVC.h"
#interface ViewController : UIViewController{
IBOutlet UITextView *textView;
}
#property (nonatomic, retain) UITextView *textView;
- (IBAction)go:(id)sender;
#end
//ViewController.m
- (IBAction)go:(id)sender{
secondVC *sec = [[secondVC alloc] init];
sec.viewController = self;
[sec print];
}
//secondVC.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface secondVC : UIViewController {
ViewController *viewController;
}
#property(nonatomic, retain)ViewController *viewController;
- (void)print;
#end
//secondVC.m
#synthesize viewController;
- (void)print{
NSString *printThis = #"This works";
self.viewController.textView.text = printThis ;
}
Try with protocol... if you want to send string from textView(child) to other ViewController(parent)
You need a delegate method that is fired from the SecondVC and handled in the first one (ViewController).
There are a few issues here:
You've got a ViewController creating a new secondVC and sending it a print message. That's okay, but the implementation of -print creates a different instance of ViewController and tries to set the text of it's textView property. That's clearly not what you want -- you should instead be sending the text back to the original instance of ViewController.
That second instance of ViewController very likely has its textView property set to nil since textView is an outlet, but you haven't loaded its view from the .xib.
It's really not nice for one view controller to mess with the views of another view controller. The secondVC should be giving the text to the original ViewController object, not trying to set the text of one of its views.
To facilitate communication from secondVC to ViewController, give secondVC a property to keep track of the original ViewController. The usual thing to do here is to define a delegate protocol for secondVC and implement that protocol in ViewController. When ViewController creates secondVC, it sets the delegate of secondVC to itself. That gives secondVC a pointer to its delegate (it shouldn't care whether its a ViewController or some other kind of object, as long as the delegate implements the right methods).
.h file:
#import <UIKit/UIKit.h>
#protocol StringDelegate <NSObject>
-(void)getArrayOfStrings:(NSMutableArray*)strArray;
#end
#interface WWSettings : UIViewController{
}
#property(nonatomic,assign)id<StringDelegate>delegate;
#end
.m file:
#import "WWSettings.h"
#implementation WWSettings
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)blablablaFunction{
[delegate getArrayOfStrings:yourArray];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#end
if you dont understand how it works .. ask ! i'll make my best to help you )
your secondVC
#import <UIKit/UIKit.h>
#import "WWSettings.h"
#interface secondVC : UIViewController<StringDelegate>{
WWSettings *obj;
}
#end
and .m file :
#import "secondVC.h"
#implementation secondVC
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)getArrayOfStrings:(NSMutableArray *)strArray{
// here you get your array !!! it's a delegate function made by you in child viewController;
}
- (void)viewDidLoad
{
obj = [[WWSettings alloc]init];
[obj setDelegate:self];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
first VC .h file :
#import <UIKit/UIKit.h>
#protocol textViewChildDelegate <NSObject>
-(void)getStrings:(NSString*)string;
#end
#interface textViewChild : UIViewController<UITextViewDelegate>{
UITextView *textView;
}
#property(nonatomic,assign)id<textViewChildDelegate>delegate;
#end
.m file:
#import "textViewChild.h"
#implementation textViewChild
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)myWorkingMethod{
// get string from textView
[delegate getStrings:textView.text];
}
- (void)viewDidLoad
{
textView = [[UITextView alloc]initWithFrame:CGRectMake(0, 240, 320, 240)];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
Now go to secondVC .h:
#import <UIKit/UIKit.h>
#import "textViewChild.h"
#interface TextViewViewController : UIViewController<textViewChildDelegate>{
UITextView * myfirstTextView;
}
#end
and to .m file:
#import "TextViewViewController.h"
#implementation TextViewViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)getStrings:(NSString *)string{
myfirstTextView.text = string; // finally we get string from child view controller
}
- (void)viewDidUnload
{
myfirstTextView = [[UITextView alloc]init];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
I'm getting the error mentioned in the title.
Here's my interface:
#import <UIKit/UIKit.h>
#class ControllerLevel1;
#interface RootController : UIViewController {
ControllerLevel1 *controllerLevel1;
}
#property (retain, nonatomic) ControllerLevel1 *controllerLevel1;
#end
And here's the implementation:
#import "RootController.h"
#import "ControllerLevel1.h"
#implementation RootController
#synthesize controllerLevel1;
- (void)viewDidLoad {
ControllerLevel1 *firstLevel = [[ControllerLevel1 alloc]initWithNibName:#"ControllerLevel1" bundle:nil];
self.controllerLevel1 = firstLevel;
[self.view insertSubview:firstLevel.view atIndex:0];
[firstLevel release];
[super viewDidLoad];
}
The error occurs in [self.view insertSubview:firstLevel.view atIndex:0];
It's complaining about firstLevel.view.
So, likely you didn't declare "view" as a property in ControllerLevel1.h. Show us the .h file for that and we can help more.