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.
Related
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.
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 want to make a multiple windows mac application and I am stuck with the multiple window part !
I can display some windows by creating a xib file and using this method :
-(void)popView:(NSString *)viewName {
_windowController = [[AddProdutWindowController alloc] initWithWindowNibName:viewName];
[_windowController showWindow:nil];
}
With
#property (strong, nonatomic) AddProdutWindowController *windowController;
in my header file and AddProductViewController inherit from NSWindowViewController
I have linked a subclass of NSViewController in Xcode to my xib file.
Now I want to send some datas to my new view and show them in some NSTextField and I have not a single clue how to do it !
I'm very confused with the WindowsController and ViewController, I don't exactly know how / where use them.
Thank you for helping.
Try this:
your firstWindowController.h:
#import <Cocoa/Cocoa.h>
#class SecondWindowController;
#interface ResultWindowController : NSWindowController{
SecondWindowController *swc;
}
#property (assign) NSNumber *xyz;
...
your firstWindowController.m:
#import "FirstWindowController.h"
#import "SecondWindowController.h"
#implementation FirstWindowController
#synthesize xyz;
- (IBAction)openSecondWindow:(id *)sender {
if (!swc)
{
swc = [[SecondWindowController alloc] init];
}
[Swc setFwc:self];
[Swc showWindow:self];
your secondWindowController.h:
#import <Cocoa/Cocoa.h>
#class FirstWindowController;
#interface SecondWindowController : NSWindowController {
FirstWindowController *fwc;
}
#property (retain) IBOutlet FirstWindowController *fwc;
#end
your secondWindowController.m:
#import "SecondWindowController.h"
#import "FirstWindowController.h"
#implementation SecondWindowController
#synthesize fwc;
- (id)init
{
if(![super initWithWindowNibName:#"SecondWindow"])
return nil;
NSLog(#"_init: %#", NSStringFromClass([self class]));
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
NSLog(#"swc didload self=%p", self); //thats your second window controller
NSLog(#"fwc value is %#", fwd.xyz); // here you should be able to see the value from FirtsWindowController
}
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'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.