NSNotification not being received in another class - objective-c

I'm trying to send a notification from one class to another, but notificationReceived never gets called.
AppDelegate:
#import <Cocoa/Cocoa.h>
#import "TestManager.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#end
------------------------------
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSLog(#"did finish launching");
[[TestManager alloc] init];
}
#end
Manager:
#import <AppKit/AppKit.h>
#import "TestObserver.h"
#interface TestManager : NSObject
#end
------------------------------
#import "TestManager.h"
#implementation TestManager
- (instancetype)init {
self = [super init];
if (self) {
NSLog(#"manager init");
[[TestObserver alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationReceived) name:#"testnotification" object:nil];
}
return self;
}
- (void)notificationReceived {
NSLog(#"notification received");
}
#end
Observer:
#import <AppKit/AppKit.h>
#interface TestObserver : NSObject
#end
------------------------------
#import "TestObserver.h"
#implementation TestObserver
- (instancetype)init {
self = [super init];
if (self) {
NSLog(#"observer init");
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(sendNotification) userInfo:nil repeats:YES];
}
return self;
}
- (void)sendNotification {
NSLog(#"observer post");
[[NSNotificationCenter defaultCenter] postNotificationName:#"testnotification" object:nil];
}
#end

Both TestManager and TestObserver need to be a property/ivar, or else either one is deallocated too early.

Related

Delegate not informing my View

My cpViewController has a button that modal segues to cpScanViewController. I want to have cpScanViewController inform ViewController with a string when it has completed a successful scan. After reading numerous articles online, I believe delegation is the best way to do this?
Is the delegate being set correctly?
How does the delegate inform the cpViewController?
cpScanViewController.h
#import <UIKit/UIKit.h>
#protocol ScanDelegate <NSObject>
-(void)receivedUPC:(NSString *)Scan;
#end
#interface cpScanViewController : UIViewController
#property (nonatomic, weak) id <ScanDelegate> delegate;
#property (nonatomic, retain) NSString *UPC;
-(void)checkUPC;
#end
cpScanViewController.m
#interface cpScanViewController ()
{
NSString *UPC;
}
#end
#implementation cpScanViewController
#synthesize delegate;
- (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.
}
-(void)doSomeScanStuff
{
UPC = #"a string"
// even tried
[delegate receivedUPC:UPC];
}
-(void)checkUPC
{
}
#end
cpViewController.h
#import <UIKit/UIKit.h>
#import "cpScanViewController.h"
#interface cpViewController : UIViewController <ScanDelegate>
#end
cpViewController.m
#interface cpViewController ()
{
cpScanViewController *cp;
}
#end
#implementation cpViewController
- (void)viewDidLoad
{
// set delegate
cp = [[cpScanViewController alloc] init];
[cp setDelegate:self];
}
-(void)receivedUPC:(NSString *)Scan{
// Nothing happening
NSLog(#"%#", Scan);
NSLog(#"The %#", cp.UPC);
}
#end
Because you are not calling receivedUPC method from your cpScanViewController class. You need to add [_delegate receivedUPC:self], some where in order to invoke this delegat method in cpViewController class.
I was able to get it working by using the method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
cpScanViewController *vd = (cpScanViewController *)[segue destinationViewController];
vd.delegate = self;
}
Because I made the connection in storyboards I didn't realize it needed to have the method.

pass NSString variable to other class with NSNotification

I want to pass a NSString from one class to another class and add that NSString to an NSMutableArray in my second class. I'm believe i can use NSNotification for this, but i don't know how to pass an variable over notification. My code would something like this:
//class1.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property(strong,nonatomic)NSString *variableString;
#end
//class1.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize variableString = _variableString;
- (void)viewDidLoad
{
[super viewDidLoad];
[self setVariableString:#"test"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"pasteString" object: _variableString];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#end
//class2.h
#import <UIKit/UIKit.h>
#interface ViewController2 : UIViewController
#property(strong,nonatomic)NSMutableArray *arr;
#end
//class2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
#synthesize arr = _arr;
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad];
if(_arr == nil)
{
_arr = [[NSMutableArray alloc]init];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"pasteString" object:nil];
// Do any additional setup after loading the view.
}
- (void) incomingNotification:(NSNotification *)notification{
NSString *theString = [notification object];
[_arr addObject:theString];
}
#end
In sender class you can post a notification with an object with something like this:
[[NSNotificationCenter defaultCenter] postNotificationName: NOTIFICATION_NAME object: myString];
The listener or receiver class has to register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:NOTIFICATION_NAME object:nil];
The method incomingNotification is:
- (void) incomingNotification:(NSNotification *)notification{
NSString *theString = [notification object];
...
}
EDIT
When you post the notification from "ViewController", is "ViewController2" loaded?
Send your Notification like this:
if(isComingFromHowToUseThisApp) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"noteFromVideoPlayer"
object:#"fromHowToUseThisApp"];
}else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"noteFromVideoPlayer"
object:#"fromVideoPlayerViewiPad"];
}
Receive your notification like this:
// Reset the default value of table view in master split view
- (void)defaultCellSelectionOfTableView:(NSNotification *) objNotification {
// We need to change the default selection of row
NSString *noticeFromVideoPlayer = [objNotification object];
}// end defaultCellSelectionOfTableView

Set label on another view to stored NSDate

I have an app with mulitple views. On view one I create and NSDate as follows:
NSString *storeDate = [[NSDate date] description];
On view 2, in viewDidLoad: I want to set the value of a label (outlet etc created and linked) to storeDate value using
timeRecord.text = storeDate;
I have imported my views but storeDate is being flagged as undeclared.
Any idea how I can get this to work?
SOFViewController.h
#import <UIKit/UIKit.h>
#interface SOFViewController : UIViewController {
}
-(IBAction) storeDateBut: (id) sender;
-(IBAction) goToView2: (id) sender;
#property (nonatomic,retain) NSString *storeDate;
#end
SOFViewController.m
#import "SOFViewController.h"
#import "view2.h"
#implementation SOFViewController
#synthesize storeDate;
-(IBAction) storeDateBut: (id) sender{
self.storeDate = [[NSDate date] description];
}
-(IBAction) goToView2: (id) sender{
view2 *myview2 = [[view2 alloc] initWithNibName:#"view2" bundle:nil];
[self.view addSubview:myview2.view];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[super dealloc];
}
#end
view2.h
#import <UIKit/UIKit.h>
#interface view2 : UIViewController {
IBOutlet UILabel *dateLabel;
}
-(IBAction) goToView1: (id) sender;
#property (nonatomic, retain) IBOutlet UILabel *dateLabel;
#end
view2.m
#import "view2.h"
#import "SOFViewController.h"
#implementation view2
#synthesize dateLabel;
-(IBAction) goToView1: (id) sender{
SOFViewController *mySOFViewController = [[SOFViewController alloc] initWithNibName:#"view2" bundle:nil];
[self.view addSubview:mySOFViewController.view];
}
- (void)viewDidLoad {
dateLabel.text = SOFViewController.storeDate;
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
#end
Scope of storeDate is limited to method of view1. So you can not access it other views or even other method of same class. you have to make "storeDate" property then you can access it anywhere like
#interface view1:UIView {}
#property(nonatomic, retain) NSString *storeDate;
#end
in implementation use
#synthesize storeDate;
now set the value of storeDate as
self.storeDate = [[NSDate date] description];
and you can use it any where.
I assume from your question you mean you have multiple view controllers in your app.
storeDate is only local to the method you've declared it in. I think you need to read up on variable scope in Objective-c
use this code.
in view 2.
#import "view2.h"
#import "SOFViewController.h"
#implementation view2
#synthesize dateLabel;
-(IBAction) goToView1: (id) sender{
SOFViewController *mySOFViewController = [[SOFViewController alloc] initWithNibName:#"view2" bundle:nil];
[self.view addSubview:mySOFViewController.view];
}
- (void)viewDidLoad {
dateLabel.text = mySOFViewController.storeDate;
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
#end

Can an object subscribe to its own notifications?

For example, a UITextField cannot be its own delegate, but is it OK to just have it register itself as an observer of its own notifications? Looks weird but seems to work fine. Thoughts?
// MyTextField.h
#interface MyTextField : UITextField
#end
// MyTextField.m
#interface MyTextField ()
- (void)myTextFieldDidChange:(NSNotification *)notification;
#end
#implementation MyTextField
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(myTextFieldDidChange:)
name:UITextFieldTextDidChangeNotification
object:self];
}
}
- (void)myTextFieldDidChange:(NSNotification *)notification {
// Do custom stuff here.
}
#end
What you're doing seems fine, but there's a purer solution for this particular example:
// MyTextField.h
#interface MyTextField : UITextField
#end
// MyTextField.m
#interface MyTextField ()
- (void)myTextFieldDidChange:(UITextField *)textField;
#end
#implementation MyTextField
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addTarget:self action:#selector(myTextFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
}
return self;
}
- (void)myTextFieldDidChange:(MyTextField *)myTextField {
// Do custom stuff here.
}
#end
Check out the UIControlEvents reference.

EXC_BAD_ACCESS when I change moviePlayer contentURL

In few words, my application is doing that :
1) My main view (RootViewController) has a buton when I tap on it, it displays the player (PlayerViewController) :
2) In my Player, I initialize the video I want to play
-> It's working good, my movie is display
My problem :
When I go back to my main view :
And I tap again on the button, I get a *Program received signal: “EXC_BAD_ACCESS”.*
If I comment self.player.contentURL = [self movieURL]; it's working, but when I let it, iI have this problem.
I read that it's due to null pointer or memory problem but I don't understand why it's working the first time and not the second time. I release my object in dealloc method.
Thanks for your help !
Bruno.
Here is my code :
Root View Controller
RootViewController.h
#import <UIKit/UIKit.h>
#import "PlayerViewController.h"
#interface RootViewController : UIViewController {
IBOutlet UIButton * myButton;
}
#property (nonatomic,retain) IBOutlet UIButton * myButton;
-(IBAction)displayPlayer:(id)sender;
- (void) returnToRoot: (PlayerViewController *) controller;
#end
RootViewController.m
#import "RootViewController.h"
#implementation RootViewController
#synthesize myButton;
-(IBAction)displayPlayer:(id)sender
{
PlayerViewController *playerViewController = [[PlayerViewController alloc] initWithNibName:#"PlayerViewController" bundle:nil];
playerViewController.delegate = self;
playerViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController: playerViewController animated: YES];
[playerViewController release];
}
- (void) returnToRoot: (PlayerViewController *) controller
{
[self dismissModalViewControllerAnimated: YES];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
#end
Player View Controller
PlayerViewController.h
#import <UIKit/UIKit.h>
#import <MediaPlayer/MPMoviePlayerController.h>
#protocol PlayerViewControllerDelegate;
#interface PlayerViewController : UIViewController {
UIView *viewForMovie;
MPMoviePlayerController *player;
}
#property (nonatomic, assign) id <PlayerViewControllerDelegate> delegate;
#property (nonatomic, retain) IBOutlet UIView *viewForMovie;
#property (nonatomic, retain) MPMoviePlayerController *player;
- (NSURL *)movieURL;
-(IBAction)goBackToRoot:(id)sender;
#end
#protocol PlayerViewControllerDelegate
- (void) returnToRoot: (PlayerViewController *) controller;
#end
PlayerViewController.m
#import "PlayerViewController.h"
#implementation PlayerViewController
#synthesize player;
#synthesize viewForMovie;
#synthesize delegate;
- (void)dealloc {
[super dealloc];
[player release];
[viewForMovie release];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"viewDidLoad");
self.player = [[MPMoviePlayerController alloc] init];
[self.player autorelease];
self.player.view.frame = self.viewForMovie.bounds;
self.player.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.viewForMovie addSubview:player.view];
self.player.contentURL = [self movieURL];
[self.player play];
}
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath =
[bundle
pathForResource:#"myVideo"
ofType:#"mp4"];
if (moviePath) {
return [NSURL fileURLWithPath:moviePath];
} else {
return nil;
}
}
-(IBAction)goBackToRoot:(id)sender{
[self.delegate returnToRoot: self];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
#end
Problem
The second time I call "displayPlayer" I had the EXC_BAD_ACCESS
I solved it !!!
I look on the MPMoviePlayerController to see what kind of variable is contentURL
(NSURL *)contentURL
It means I have also to liberate it.
I do that in my dealloc method putting a nil value:
-(void) dealloc {
[super dealloc];
self.player.contentURL = nil;
[player release];
[viewForMovie release];
}
If I comment self.player.contentURL =
[self movieURL]; it's working, but
when I let it, iI have this problem.
In that case, how is contentURL declared? Does the #property definition include copy or retain?