I'm trying to change a label from the AppDelegate. I can change the label with an IBAction that runs a changeLabel in the implementation of the class that has the label, but if I try to run changeLabel from the AppDelegate it changes the value (I have an NSLog), but doesn't update label.
Here's the code:
#import <Foundation/Foundation.h>
#interface testLabelThingy : NSObject
#property (strong) IBOutlet NSTextField *daLabel;
- (id) init;
- (void)changeLabel;
- (IBAction)daButton:(id)sender;
#end
and:
#import "testLabelThingy.h"
#implementation testLabelThingy
#synthesize daLabel;
- (id) init{
self.daLabel = [[NSTextField alloc] init];
return self;
}
- (IBAction)daButton:(id)sender{
[self changeLabel];
}
- (void)changeLabel{
NSLog(#"Change Label Function. Current value is: %#", [self.daLabel stringValue]);
if([[self.daLabel stringValue] isEqualToString:#"Bloog"]){
[self.daLabel setStringValue:#"Blarg"];
}else{
[self.daLabel setStringValue:#"Bloog"];
}
}
#end
For that you have to use NSNotificationCenter.
in Appdelegate use following code.
[[NSNotificationCenter defaultCenter] postNotificationName:#"ChangeThelabel" object:nil];
use the below code in the init method of the implementation of the class that has the label.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ChangelabelText:) name:#"ChangeThelabel" object:nil];
And in the same class use the following function.
- (void)ChangelabelText:(NSNotification *)notification
{
// Change the text here.
}
Related
I'm quite new to Mac programming (not to Objective C).
I'm developing a small application, that shows some data and opens a second window on button press.
In the second window is a textfield and a submit button. If the submit button is pressed, the window should close + the value of the textfield needs to be passed to the first window.
I think the best method for that is a simple Delegate. I tried that but i can't change the label in the first window using the second window..
The delegate however seems to work as i can call methods from the other class and send data to it. It just won't change the label.
As this is my first try on Delegates, im pretty sure I've done something stupid here^^
or is there a better solution? Can't be to complicated to change a label from an second window.. right?
ViewController.h (FirstController)
#import <Cocoa/Cocoa.h>
#class ViewController;
#protocol ViewControllerDelegate
-(void)sayHello:(ViewController *)ViewController;
#end
#interface ViewController : NSViewController
{
IBOutlet NSTextField *txtlabel;
}
#property (nonatomic, assign) id delegate;
-(void)helloDelegate;
-(void)reciveVar:(NSString*)strvar;
#end
ViewController.m (FirstController)
#import "ViewController.h"
#implementation ViewController
#synthesize delegate;
-(id)init {
self = [super init];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
txtlabel.stringValue=#"TEST";
}
-(void)helloDelegate
{
[delegate sayHello:self];
}
-(void)reciveVar:(NSString*)strvar
{
NSLog(#"recived: %#", strvar);
txtlabel.stringValue=strvar; // DOSENT WORK!!
}
#end
secondController.h
#import <Cocoa/Cocoa.h>
#import "ViewController.h"
#interface secondController : NSViewController <ViewControllerDelegate>
{
IBOutlet NSTextField *txtfield;
}
-(IBAction)submit:(id)sender;
#end
secondController.m
#import "firstController.h"
#implementation secondController
-(void)viewDidLoad
{
[super viewDidLoad];
ViewController *custom = [[ViewController alloc] init];
// assign delegate
custom.delegate = self;
[custom helloDelegate];
}
-(void)sayHello:(ViewController *)ViewController
{
NSLog(#"Hiya!");
}
-(IBAction)submit:(id)sender
{
NSString *txtval= txtfield.stringValue;
NSLog(#"submit: %#", txtval);
ViewController *custom = [[ViewController alloc] init];
// assign delegate
custom.delegate = self;
[custom reciveVar:txtval];
}
#end
LOG Output:
Hiya!
submit: test
recived: test
(so i guess the delegate works..)
SOLVED. (Thanks to Phillip Mills)
NSNotification is way simpler and efficient than Delegates in this case.
ViewController.m
[...]
- (void)viewDidLoad
{
[super viewDidLoad];
txtlabel.stringValue=#"TEST";
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleUpdatedData:)
name:#"DataUpdated"
object:nil];
}
-(void)handleUpdatedData:(NSNotification *)notification
{
NSLog(#"recieved %#", notification);
txtlabel.stringValue=[notification object];
}
secondController.m
-(IBAction)submit:(id)sender
{
NSString *txtval= txtfield.stringValue;
NSLog(#"submit: %#", txtval);
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:txtval];
}
In my ViewController.m I have my void "Save Data":
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myDatePicker, myTextField;
-(void)saveData
{
NSArray *value = [[NSArray alloc] initWithObjects:[myTextField text],[myDatePicker date], nil];
[value writeToFile:[self getFilePath] atomically:YES];
}
And I want to use my void "Save Data" in my AppDelegate.m :
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.ViewController saveData];
}
But Xcode doesn't recognize the "ViewController" neither "saveData".
I don't know if I have to #import something in my AppDelegate, please help.
How you access your viewcontroller from the app delegate will depend on how many viewcontrollers you have / where it is in your current navigation hierarchy, etc etc. You could add a property for it, or perhaps it will be your app delegate's rootViewController.
However, you'd probably be better off listening out for a notification in your viewcontroller when the app enters the background. This means that the logic you need can be entirely self contained within your viewcontroller, and your app delegate doesn't need to know anything about it.
In your viewcontroller's initialization method, you can register to receive notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
Then, implement the method:
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
[self saveData];
}
And make sure you also remove yourself as an observer in your viewcontroller's dealloc method:
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
In your "appDelegate.m" implementation file at the top do this import ->
#import "ViewController.h"
This will make the custom class ViewController visible to the appDelegate.
In order to be able to call the -(void)saveData method from another class (in our case the other class is the AppDelegate class), this method needs to be declared in the public interface. That means ->
#import "ViewController.h"
#interface ViewController ()
-(void)saveData;
#end
I'm writing a function of QTMovieView. I want to double click on the QTMovieView to make it exit full screen mode. The QTMovieView is control by AppController.m, and I write the exit fullscreenmode function in the AppController. Because I want to capture the event of double click the QTMovieView. So I have to override the mouseDown event. The Override function is write in the "QTMovieView+TFOverrideDrag.h"
QTMovieView+TFOverrideDrag.m
#import "QTMovieView+TFOverrideDrag.h"
#include "AppController.h"
#implementation QTMovieView (TFOverrideDrag)
- (void)mouseDown:(NSEvent *)theEvent
{
[self.superview becomeFirstResponder];
NSInteger clickCount = [theEvent clickCount];
if (2 == clickCount) {
[AppController exitFullScreen:self];
NSLog(#"SS");
}
NSLog(#"MDown");
}
and this function override successfully. but the exitFullScreen function fail. how can I fix it? Thanks
Update
AppController.h
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
#import <QTKit/QTKit.h>
#interface AppController : NSDocument
{
QTMovie *qtmovie;
QTMovieView *_movieView;
}
#property (assign) IBOutlet QTMovieView *movieView;
- (IBAction)toggleFullscreen:(id)sender;
+(IBAction)exitFullScreen:(id)sender;
#end
AppController.m
#import "AppController.h"
#implementation AppController
#synthesize movieView=_movieView;
- (IBAction)toggleFullscreen:(id)sender
{
_movieView=_movieView;
NSDictionary *fullScreenOptions = [[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]forKey:NSFullScreenModeSetting] retain];
[_movieView enterFullScreenMode:[[NSScreen mainScreen] retain] withOptions:fullScreenOptions];
}
+(void)exitFullScreen:(id)sender
{
_movieView=_movieView;
NSLog(#"exitFullscreen");
NSDictionary *fullScreenOptions = [[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]forKey:NSFullScreenModeSetting] retain];
[_movieView exitFullScreenModeWithOptions:fullScreenOptions];
}
#end
Are you sure AppController declares a class method like +(void)exitFullScreen:? If not, then you will either need to change your instance method to a class method (use a + instead of a -) or subclass the QTMovieView class and pass an instance of AppController to the instance.
problem solved!!! I fixed the problem by this :
- (void)mouseDown:(NSEvent *)theEvent
{
[self.superview becomeFirstResponder];
NSInteger clickCount = [theEvent clickCount];
if (2 == clickCount) {
NSDictionary *fullScreenOptions = [[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]forKey:NSFullScreenModeSetting] retain];
[super enterFullScreenMode:[[NSScreen mainScreen] retain] withOptions:fullScreenOptions];
NSLog(#"SS");
}
NSLog(#"MDown");
}
the key is "super".
I think I understand the logic behind a delegate. I got more the problem to use it. How many steps are involved? Do I have to use existing delegates? Or can I use my one ones?
In my example I got the AppDelegate that created many views (Objects / View Controllers) of the same type. Each view should somehow call a method on the AppDelegate to close itself. This would happen when a button within the view is touched. The method call would include the reference of the view (self).
So far I know from other languages responders, event listeners and so on. They are so simple to use.
Can anybody help me. I just found massive examples with a lot of code in the web. It can't be that hard to just call a parent in Objective C.
I think you should use for this the NSNotificationCenter
in you AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(buttonPushed:) name:#"ButtonPushedNotification" object:nil];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
...
...
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
this is the selector it will be called when the notification happens (we are still in the AppDelegate.m)
- (void)buttonPushed:(NSNotification *)notification {
NSLog(#"the button pushed...");
}
and in the ViewController.m when the button pushed (inside the method), you should post a notification like this:
{
...
[[NSNotificationCenter defaultCenter] postNotificationName:#"ButtonPushedNotification" object:nil];
...
}
You can create your own:
In MyView1.h:
#class MyView1;
#protocol MyView1Delegate <NSObject>
- (void)closeMyView1:(MyView1 *)myView1;
#end
#interface MyView1 : NSObject
{
id<MyView1Delegate> _delegate;
}
#property (assign, nonatomic, readwrite) id<MyView1Delegate> delegate;
...
#end
In MyView1.m:
#interface MyView1
#synthesize delegate = _delegate;
...
// The method that tells the delegate to close me
- (void)closeMe
{
....
if ([_delegate respondsToSelector:#selector(closeMyView1:)])
{
[_delegate closeMyView1:self];
}
}
#end
In AppDelegate.h:
#import "MyView1.h"
#interface AppDelegate <MyView1Delegate>
{
MyView1 *_myView1;
}
...
#end
In AppDelegate.m:
- (void)someCreateViewMethod
{
_myView1 = [[MyView1 alloc] initWithFrame:NSMakeRect(0, 0, 100, 200)];
[_myView1 setDelegate:self];
...
}
An easy way to get what you want is to just start with one view. Then, have each other view be presented modally. When the button in the view is pressed do
[self dismissModalViewControllerAnimated:YES];
And here's something I made a while ago when I was starting iPhone development that might help you with delegates
Delegates
//In parent .m file:
//assign the delegate
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segueName"])
{
childController *foo = segue.destinationViewController;
foo.delegate = self;
}
}
//implement protocol method(s):
- (void) methodName:(dataType*) dataName
{
//An example of what you could do if your data was an NSDate
buttonLabel.titleLabel.text = [[date description] substringToIndex:10];
}
//In parent .h file:
//import child header
#import "ChildName.h"
//indicate conformity with protocol
#interface ParentName : UIViewController <ChildNameDelegate>
//In child .h file
//declare protocol
#protocol ChildNameDelegate
- (void) methodName:(dataType*) dataName;
#end
//declare delegate
#property (unsafe_unretained, nonatomic) id<ChildNameDelegate> delegate;
//In child .m file
//synthesize delegate
#synthesize delegate;
//use method
- (IBAction)actionName:(id)sender
{
[delegate methodName:assignedData];
}
In AppDelegate.m, I've defined
#import "AppDelegate.h"
#import "allerta.h"
#implementation AppDelegate
#synthesize window = _window;
-(void)awakeFromNib {
// Add an observer that will respond to loginComplete
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(alerticonstatus:)
name:#"alert" object:nil];
// Post a notification to loginComplete
[[NSNotificationCenter defaultCenter] postNotificationName:#"alert" object:nil];
}
#end
I want to call alerticonstatus from allerta.h:
#import <Foundation/Foundation.h>
#interface allerta : NSObject{
}
-(void)alerticonstatus:(NSNotification *)note;
#end
allerta.m:
#import "allerta.h"
#implementation allerta
-(void)alerticonstatus:(NSNotification *)note {
NSLog(#"called alerticonstatus");
}
#end
Can I import a function whit #selector from another file like allerta.h?
Now I have SIGABRT error.
Can you help me? Thanks.
Change you method for this, it work:
#import "AppDelegate.h"
#import "allerta.h"
#implementation AppDelegate
#synthesize window = _window;
-(void)awakeFromNib {
allerta *_allerta = [allerta alloc]; //allocation memory
// Add an observer that will respond to loginComplete
[[NSNotificationCenter defaultCenter] addObserver:_allerta //here you called self, but you need to call your class allerta
selector:#selector(alerticonstatus:)
name:#"alert" object:nil];
[_allerta release]; //kill _allerta class if you don't need more
// Post a notification to loginComplete
[[NSNotificationCenter defaultCenter] postNotificationName:#"alert" object:nil];
}
#end
When you create class files, set firs letter big, like as "Allerta".
I think your problem is that you declare AppDelegate as receiver of alerticonstatus message when AppDelegate doesn't have this method declared. You are doing this in this line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(alerticonstatus:)
name:#"alert" object:nil];
Your solution would be to change observer from self which in this case is AppDelegate to some allerta object. Just alloc-init some allerta object and add it as observer.