NSInvalidArgumentException when posting notification - cocoa-touch

I am trying to use the notification system in order to have a detail view in a Splitviewcontroller to update the tableview. I declared the notification as follows:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pushView:) name:#"pushView" object:nil];
and the selector itself:
- (void) pushView:(UIViewController *) viewController {
[self.navigationController pushViewController:viewController animated:YES];
}
Now, in the detailview I create the view-controller and call create the notification:
ArticleTableViewController *articleTableView = [[ArticleTableViewController alloc] initWithCategory:catInt];
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushView" object:articleTableView];
I assumed that that would work, but I get the error:
* Terminating app due to uncaught
exception
'NSInvalidArgumentException', reason:
'-[NSConcreteNotification
setParentViewController:]:
unrecognized selector sent to instance
0x5a3a290'
So I guess I am doing something wrong in how including the detailViewController in the notification to be used to push in.

The method definition for handling the notification seems to be wrong.
- (void) pushView:(UIViewController *) viewController
should be,
- (void) pushView:(NSNotification *) notification
The actual notification is passed as the argument, not any view controllers. To achieve what you want, try the following.
- (void) pushView:(NSNotification *) notification
NSDictionary *userInfo = [notification userInfo];
UIViewController *viewController = (UIViewController *)[userInfo objectForKey:#"ViewController"];
[self.navigationController pushViewController:viewController animated:YES];
}
And while posting the notification,
ArticleTableViewController *articleTableView = [[ArticleTableViewController alloc] initWithCategory:catInt];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:articleTableView forKey:#"ViewController"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushView" object:nil userInfo:userInfo];

Related

How to receive 2 notifications via NSNotificationCenter

I have three methods:
- (void)viewDidAppear:(BOOL)animated
{
[self updateViews];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotification:) name:#"itemQuantityChanged" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotification:) name:[NSString stringWithFormat:#"Item %# deleted", itemUUID] object:nil];
}
- (void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void) receiveNotification: (NSNotification *)notification
{
if ([[notification name] isEqualToString:#"itemQuantityChanged"])
[self updateViews];
if ([[notification name] isEqualToString:[NSString stringWithFormat:#"Item %# deleted", itemUUID]])
NSLog(#"FAIL!");
}
The main idea is that for this class I need to receive 2 different notifications and in case of receiving them need to perform different actions. Is everything ok with the realization? I believe that it is possible to simplify this code. How to removeObserver correctly? I don't use ARC.
You should use a different selector for each notification. That way, you don't need any logic in the method to determine which notification was sent.
Removing the observers as you are doing is fine.

Change value storyboard from AppDelegate

I have a imageView in my storyboard. Now I want to set imageView to nil when the app will enter the background (applicationDidEnterBackground and applicationWillTerminate). But I couldn't find a good way to do this.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
.... imageView.image = nil; ?
}
What is a good way to change a value of imageView from AppDelegate?
I solved the problem by adding this in viewDidLoad of my viewcontroller.m file:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(removeImage)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
And the removeImage function:
- (void) removeImage {
ImageView.image = nil;
}

MPMoviePlayerViewController backgrounding deal. How to continue playing after returning from background to foreground?

I'm working on application for video streaming. It is built with ARC for iOS5.
To display view I use MPMoviePlayerViewController this way:
.h
#interface EpisodesTableViewController : UITableViewController<EpisodeUrlResolverDelegate> {
NSTimeInterval playbackTime;
EpisodeUrlResolver *episodeUrlResolver;
}
#property (strong, nonatomic) MPMoviePlayerViewController *player;
#end
.m
#implementation EpisodesTableViewController
#synthesize episodes, player;
- (void)viewDidLoad
{
[super viewDidLoad];
episodeUrlResolver = [[Soap4MeEpisodeUrlResolver alloc] init];
[episodeUrlResolver setDelegate:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterForeground) name:#"WillEnterForeground" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterBackground) name:#"WillEnterBackground" object:nil];
}
- (void)viewDidUnload
{
episodeUrlResolver = nil;
player = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"WillEnterForeground" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"WillEnterBackground" object:nil];
[super viewDidUnload];
}
- (void)url:(NSURL *)url WasResolvedForEpisode:(Episode *)episode {
player = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
player.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
player.moviePlayer.allowsAirPlay = YES;
[self presentModalViewController:player animated:YES];
}
- (void)willEnterBackground {
if (player) {
playbackTime = player.moviePlayer.currentPlaybackTime;
}
}
- (void)willEnterForeground {
if (!player)
return;
if(player.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted || player.moviePlayer.playbackState == MPMoviePlaybackStateStopped || player.moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
[self continuePlayback];
}
}
- (void)continuePlayback {
[self presentModalViewController:player animated:YES];
[player.moviePlayer setInitialPlaybackTime:playbackTime];
NSLog(#"%f", player.moviePlayer.initialPlaybackTime);
[player.moviePlayer play];
}
Few words about presented code:
when url for video is resolved I create the MPMoviePlayerViewController object for video playback. Playback is started. Then when app is going background with home button MPMoviePlayerViewController automatically pauses playback and dissapears from screen (weird behavior, as for me). Thats why I have to save playback time when app is going background and show existing MPMoviePlayerViewController again with restored playback time when app is back to foreground.
It works fine when user presses home button and then is going back to application.
But it doesn't work when user locks the device. WillEnterBackground notification is fired when
applicationWillEnterForeground is called, so for locking event it is called too.
But in case when user locks the device MPMoviePlayerViewController wasn't being dismissed properly as in situation when user presses home button. It is hidden from the screen, but calling the code
[self presentModalViewController:player animated:YES];
throws the exception
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <EpisodesTableViewController: 0x91b3950>.'
I tried to change the code to dismiss controller:
- (void)willEnterForeground {
if (!player)
return;
if(player.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted || player.moviePlayer.playbackState == MPMoviePlaybackStateStopped || player.moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
if (self.presentedViewController) {
[self dismissViewControllerAnimated:YES completion:^{
[self continuePlayback];
}];
}
else {
[self continuePlayback];
}
}
}
Now it doesn't throw the exception, but displays warning
wait_fences: failed to receive reply: 10004003
Also completion block is never called.
How to properly to solve the background/foreground continue playing with modally displayed MPMoviePlayerViewController?
Thanks for replies.
I solved the problem.
Method -willEnterBackground should look like:
- (void)willEnterBackground {
if (player) {
playbackTime = player.moviePlayer.currentPlaybackTime;
[self dismissModalViewControllerAnimated:NO];
}
}
We have to dismiss MPMoviePlayerViewController manually when app is entering background mode.
Pay attention to use only
[self dismissModalViewControllerAnimated:NO];
instead of
[self dismissMoviePlayerViewControllerAnimated];
Thats because MPMoviePlayerViewController was shown with
[self presentModalViewController:player animated:YES];

NSNotificationCenter is not sending out notifications

I am developing a system to keep track of achievements and for that I use NSNotificationCenter. When an achievement is unlocked in a object in the app a notification is sent to JMAchievementHandler which sets a string to YES and saves the progress in NSUserDefaults. My problem is that the notifications are not working probably. Here is my code in JMAchievementHandler:
- (id)init {
self = [super init];
if (self) {
//Set strings to NO
achievementOne = #"NO";
achievementTwo = #"NO";
achievementThree = #"NO";
achievementFour = #"NO";
//Add observers
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName:) name:#"achievement1" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName) name:#"achievement2" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName) name:#"achievement3" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName) name:#"achievement4" object:nil];
//Add observer to observe delegate methods
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationFromDelegate:) name:#"notificationLaunch" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationFromDelegate:) name:#"notificationEnterBackground" object:nil];
}
return self;
}
- (void)receiveNotificationWithName:(NSNotification *)notification {
if ([[notification name] isEqualToString:#"achievement1"] || [achievementOne isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementOne = #"YES";
}
else if ([[notification name] isEqualToString:#"achievement2"] || [achievementTwo isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementTwo = #"YES";
}
else if ([[notification name] isEqualToString:#"achievement3"] || [achievementThree isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementThree = #"YES";
}
else if ([[notification name] isEqualToString:#"achievement4"] || [achievementFour isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementFour = #"YES";
}
}
- (void)receiveNotificationFromDelegate:(NSNotification *)notificationDelegate
{
if ([[notificationDelegate name] isEqualToString:#"notificationLaunch"]) {
[self loadDataOnLaunch];
}
else if ([[notificationDelegate name] isEqualToString:#"notificationEnterBackground"]) {
[self saveDataOnExit];
}
}
- (void)loadDataOnLaunch
{
NSLog(#"loadDataOnLaunch");
}
- (void)saveDataOnExit
{
NSLog(#"saveDataOnExit");
}
When I try to post a notification the NSLogs are not called. I use the following code to send notifications from my AppDelegate and ViewController.
- (void)achievement1ButtonPressed:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"achievement1" object:self userInfo:nil];
}
I hope you guys can help me out. Thanks a lot
Jonas
Do you even have a method named receiveNotificationWithName? When you entered the code, the autocomplete should have barfed on that, offering receiveNotificationWithName: instead (unless you wrote it first, without NSNotification*, then added it later...
Anyway, there is a big difference between the two. Also, your handler is buggy. You should revisit that code.
There are lots of reasons to prefer blocks, and this points to one of them. You can just put your code right in the registration, and not worry about giving the wrong selector.
[[NSNotificationCenter defaultCenter] addObserverForName:#"achievement1"
object:nil
queue:nil
usingBlock:^{
// Handle achievement1 right in this little block.
}];
These look like one-shot notifications, so if you want to unregister the notification handler after it runs one time, do this (note the __block).
__block id achievement1 = [[NSNotificationCenter defaultCenter]
addObserverForName:#"achievement1 ":^{
object:nil
queue:nil
usingBlock:^{
// Handle achievement1 right in this little block.
// Remove observer from notification center
[[NSNotificationCenter defaultCenter] removeObserver:achievement1];
}];
All of this looks like it should work (besides the colon, like Andrea mentioned). Is it possible that your button callback isn't being called for a similar reason? I would add an NSLog in your achievement1ButtonPressed: method to make sure.
What I can see is that only one of your addObesrver method call the right selector with the ":", thus the right method signature.

iPad videoPlayerDidFinishPlaying callback method not responding

I am facing a problem in ipad video incorporating. My code works fine I mean it plays the video, but once the video reaches to its end. The callback method is not called.
This method is called when play video button is pressed.
-(IBAction) playVideo : (id) sender
{
[self initPlayingVideo:#"toyVid.mp4"];
}
This method handles the playing of video.
-(void) initPlayingVideo: (NSString *) videoFile
{
NSString *moviePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:videoFile];
theMovie = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:moviePath]];
theMovie.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
theMovie.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
[self.view addSubview:theMovie.view];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(videoPlayerDidFinishPlaying
name:MPMoviePlayerPlaybackDidFinishNotification
object:theMovie];
videoPlayer = [theMovie moviePlayer];
[videoPlayer play];
}
This is the callback method.
-(void) videoPlayerDidFinishPlaying: (NSNotification*)aNotification
{
theMovie = [aNotification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:theMovie.moviePlayer];
[videoPlayer stop];
[theMovie.moviePlayer release];
[videoPlayer release];
[theMovie.view removeFromSuperview];
}
Where I am doing mistake? Please guide.
Regards
Ranjan
Did you miss the : and ) in your selector? I guess ) may be your typo otherwise you cannot compile your codes. Your selection takes one parameter. It should be:
selector:#selector(videoPlayerDidFinishPlaying:)
That will match to your instance method. I guess you don't have one without parameter.