Parent VeiwControllers of ViewControllers - iphone

My main view PlayGameViewController has a subview (actually 2 of them) called CardViewController.
CardViewController creates some buttons programmatically
-(void) initialiseButtons
{
NSLog(#"initialiseButtons %d",totalButtons);
int ypos = playerImage.frame.origin.y + playerImage.frame.size.height + 42;
for(int i=0; i<totalButtons; i++)
{
StatView *sv = [[StatView alloc] initWithYPos:ypos];
sv.tag = 100 + i;
[sv.overlayButton addTarget:self action:#selector(statTapped:)
forControlEvents:UIControlEventTouchUpInside];
sv.overlayButton.tag = 10 + i;
[self.frontView addSubview:sv];
ypos += 26;
}
}
It sets a callback function statTapped. This works ok and the function does get called.
But... All the game logic is in PlayGameViewController so I need to handle the function there. I tried deleting the function from CardViewController and implementing it instead in the PlayGameViewController but the call wasn't passed down to the parent class.
Is there away to achieve this or am I talking and thinking crazy?

I think you have a couple of options:
Create a delegate to process your statTapped: method. You can learn more about delegates and how to create one here: How do I create delegates in Objective-C?
Use NSNotification and add a listener in PlayGameViewController that will call the statTapped: method (also defined in PlayGameViewController. Then in CardViewController have your selector action call a method that posts a notification. PlayGameViewController will receive that notification and then run the specified tasks.
Here is an example of using NSNotification:
In your PlayGameViewController's init method, write:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(statTapped:)
name:#"statTapped"
object:nil];
In CardViewController, you'll want to set a selector to your button action as you do now, but instead of statTapped: you'll want a different method which contains this code:
[[NSNotificationCenter defaultCenter] postNotificationName:#"statTapped"
object:self];
By doing so your PlayGameController will own the statTapped method.
Don't forget to remove the observer in your viewDidUnload method:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"statTapped" object:nil];

Related

Get Notification when a video starts or stops in UIWebView

Hello i am new to objective - c
I'm having a problem with the UIWebView and MPMoviePlayerController: My UIWebView has a movie inside the html (it's a local html file), I'm using html5 and a video tag for the video.
I want a notification when video starts or stops in UIWebView....
I have tried using MPMoviePlayerPlaybackDidFinishNotification, but it doesnt fire ...
I have also tried to make the my main UIViewController's view a view of my own, and intercept -didAddSubview: and -willRemoveSubview:. but with no sucess...
Does any body know how to get notification from uiwebview??
You can observe #"MPAVControllerPlaybackStateChangedNotification" (use nil for the object). This notification isn't documented so I don't know if the App Store will approve your app.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateDidChange:)
name:#"MPAVControllerPlaybackStateChangedNotification"
object:nil];
The notification has the key MPAVControllerNewStateParameter in its userInfo. The value seems to be 0 before playback starts, 1 when it is paused, 2 when it is playing, and 3 (momentarily) when you are dragging the playback slider.
- (void)playbackStateDidChange:(NSNotification *)note
{
NSLog(#"note.name=%# state=%d", note.name, [[note.userInfo objectForKey:#"MPAVControllerNewStateParameter"] intValue]);
}
I searched alot about this..Here is the solution that I have found for getting the playback end notification call. Tested code on iOS6.0 and above. All thanks to #Morten.
In viewDidLoad add observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackDidEnd:)
name:#"MPAVControllerItemPlaybackDidEndNotification"//#"MPAVControllerPlaybackStateChangedNotification"
object:nil];
Then simply add following javascript code webViewDidFinishLoad delegate as below
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//http://stackoverflow.com/a/12504918/860488
[videoView stringByEvaluatingJavaScriptFromString:#"\
var intervalId = setInterval(function() { \
var vph5 = document.getElementById(\"video-player\");\
if (vph5) {\
vph5.playVideo();\
clearInterval(intervalId);\
} \
}, 100);"];
}
- (void)playbackDidEnd:(NSNotification *)note
{
//do your stuff here
[videoView removeFromSuperview];
videoView.delegate = nil;
videoView = nil;
}
You will get playbackDid End call in the above selected and can do whatever is your requirement.
Happy Coding !!

Unhiding a view is very slow in CTCallCenter callEventHandler

Reposting with more concise and focused question after original question went unanswered. Also adding more insight into the problem after another day of research:
In my app delegate (didFinishLaunching), I set up a callEventHandler on CTCallCenter.
The idea is that when a callState changes, I post a notification with a userInfo dict
containing the call.callState. In my view, I observe this notification, and when the
userInfo dict contains a value of CTCallDisconnected, I want to unhide a view.
The problem I'm having is that the unhiding aspect is taking, almost consistenly, ~ 7 seconds.
Everything else is working fine, and I know this because I NSLog before and after the unhiding,
and those logs appear immediately, but the darned view still lags for 7 seconds.
Here's my code:
appDidFinishLaunching:
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall* call) {
// anounce that we've had a state change in our call center
NSDictionary *dict = [NSDictionary dictionaryWithObject:call.callState forKey:#"callState"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CTCallStateDidChange" object:self userInfo:dict];
};
I then listen for this notification when a user taps a button that dials a phone number:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Then, in ctCallStateDidChange:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
I've tracked the problem down to the if condition in the above code sample:
if ([[userInfo valueForKey:#"userInfo"] valueForKey:#"callState"] == CTCallStateDisconnected) {
If I simply replace that with:
if (1 == 1) {
Then the view appears immediately!
The thing is, those NSLog statements are logging immediately, but the view is
lagging in it's unhiding. How could that condition cause only part of it's block
to execute immediately, and the rest to wait ~ 7 seconds?
Thanks!
Try changing your code to this:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
Note:
The parameter is an NSNotification, not an NSDictionary
I would not compare strings with ==
No need to cast the view to change the hidden property
Use NO instead of false
Update: Got an idea: Could you try the following, please, in between the NSLogs?
dispatch_async(dispatch_get_main_queue(), ^{
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
});
Reading the CTCallCenter doc, it seems the callEventHandler is dispatched on "the default priority global dispatch queue", which is not the main queue where all the UI stuff happens.
Looks like there is no problem with your hidden code. If I were you, I would comment out all the code after the call ends, and uncomment them one by one to see what is the problem.
Hm... try to call [yourViewController.view setNeedsDisplay] after you change hidden property. Or avoid hidden, use alpha or addSubview: and removeFromSuperview methods instead.
djibouti33,
Where you put this sentence to listen when a user taps a button that dials a phone number?on WillResignActive function?
this sentence --> [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Thanks for your time,
Willy.

Specific expression in if condition causes 7 second delay in execution [duplicate]

Reposting with more concise and focused question after original question went unanswered. Also adding more insight into the problem after another day of research:
In my app delegate (didFinishLaunching), I set up a callEventHandler on CTCallCenter.
The idea is that when a callState changes, I post a notification with a userInfo dict
containing the call.callState. In my view, I observe this notification, and when the
userInfo dict contains a value of CTCallDisconnected, I want to unhide a view.
The problem I'm having is that the unhiding aspect is taking, almost consistenly, ~ 7 seconds.
Everything else is working fine, and I know this because I NSLog before and after the unhiding,
and those logs appear immediately, but the darned view still lags for 7 seconds.
Here's my code:
appDidFinishLaunching:
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall* call) {
// anounce that we've had a state change in our call center
NSDictionary *dict = [NSDictionary dictionaryWithObject:call.callState forKey:#"callState"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CTCallStateDidChange" object:self userInfo:dict];
};
I then listen for this notification when a user taps a button that dials a phone number:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Then, in ctCallStateDidChange:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
I've tracked the problem down to the if condition in the above code sample:
if ([[userInfo valueForKey:#"userInfo"] valueForKey:#"callState"] == CTCallStateDisconnected) {
If I simply replace that with:
if (1 == 1) {
Then the view appears immediately!
The thing is, those NSLog statements are logging immediately, but the view is
lagging in it's unhiding. How could that condition cause only part of it's block
to execute immediately, and the rest to wait ~ 7 seconds?
Thanks!
Try changing your code to this:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
Note:
The parameter is an NSNotification, not an NSDictionary
I would not compare strings with ==
No need to cast the view to change the hidden property
Use NO instead of false
Update: Got an idea: Could you try the following, please, in between the NSLogs?
dispatch_async(dispatch_get_main_queue(), ^{
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
});
Reading the CTCallCenter doc, it seems the callEventHandler is dispatched on "the default priority global dispatch queue", which is not the main queue where all the UI stuff happens.
Looks like there is no problem with your hidden code. If I were you, I would comment out all the code after the call ends, and uncomment them one by one to see what is the problem.
Hm... try to call [yourViewController.view setNeedsDisplay] after you change hidden property. Or avoid hidden, use alpha or addSubview: and removeFromSuperview methods instead.
djibouti33,
Where you put this sentence to listen when a user taps a button that dials a phone number?on WillResignActive function?
this sentence --> [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Thanks for your time,
Willy.

Post notification from specific object - cocoa touch

So I have this function inside NotificationManager class.
-(void) postNotificationForClassName:(NSString*)className withObjects:(NSArray*)objects withError:(BOOL)withError
{
notificationName = [NSString stringWithFormat:#"didLoad%#",className];
[[NSNotificationCenter defaultCenter]
postNotificationName:notificationName object:self];
}
now, let's say I have 2 classes , A and B.
from A's method foo() I do the following:
[[NotificationManager sharedManager]
postNotificationForClassName:#"A" withObjects:objects withError:NO]
from B's method goo() I do the following:
[[NotificationManager sharedManager]
postNotificationForClassName:#"A" withObjects:objects withError:NO]
Now, I'm curious what should I do in case I want to listen only to notifications being posted from Class A.
Is this suppose to work ?
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didLoadData:)
name:#"didLoadA" object:classAObject];
Cause I'm assuming that when I'm calling
[[NSNotificationCenter defaultCenter]
postNotificationName:notificationName object:self];
and I pass the "self", then the "self" will be the NotificationManager and not the class A or B that called the NotificationManager method.
Am I right or wrong here ? and if I'm right, is there a way to do what I want to accomplish?
Thanks!
self is a special variable, always referring to the object that received the message currently being handled. In NotificationManager's -postNotificationForClassName:withObjects:objectswithError:withError:, self is a NotificationManager (or descendent). As for self in the call to -addObserver:..., it depends on what method the call occurs in.
As you've noticed, -addObserver:... lets you monitor notifications from specific objects. You could use this to monitor messages from As if you use the class A as the object:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didLoadData:)
name:#"didLoadA" object:[A class]];
However, you'll also need to change the notification sender in -postNotificationForClassName:withObjects:objectswithError:withError:.
-(void)postNotificationForClassName:(NSString*)className
withObjects:(NSArray*)objects
withError:(BOOL)withError
from:(id)sender
{
notificationName = [NSString stringWithFormat:#"didLoad%#",className];
[[NSNotificationCenter defaultCenter]
postNotificationName:notificationName object:[sender class]];
}

Why is MPMovieDurationAvailableNotification only getting dispatched once for my many instances of my MPMoviePlayerController?

I've got a UITableView which lists movie files from on disk. For each cell row, there is a worker instance allocated for each visible row, used to generate a thumbnail for the movie file and get its duration to display in the row.
For each instance of MPMoviePlayerController in the worker class Im listening for a MPMovieDurationAvailableNotification event from the movie player. For some reason this event only seems to be dispatched (or at least Im only able to catch it) from one of the worker instances. Here is the init and listener code. There are a few comments inline.
- (id) initWithRequestAsset:(RequestAsset *)asset {
if (self = [super init]) {
self.requestAsset = asset;
self.moviePlayer = [MPMoviePlayerController alloc];
[self setupMoviePlayerListeners];
[self.moviePlayer initWithContentURL:self.requestAsset.urlPath];
self.moviePlayer.shouldAutoplay = NO;
// I've also tried to retain the moviePlayer, to no avail
[self.moviePlayer release];
}
return self;
}
- (void) setupMoviePlayerListeners {
//
// If the object: is set to nil then Im able to catch three notifications, but they are all from last instance of the MPMoviePlayerController
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(onMovieDurationAvailable:)
name:MPMovieDurationAvailableNotification
object:self.moviePlayer];
}
- (void) onMovieDurationAvailable:(NSNotification *)notification {
NSLog(#"duration received notification");
self.requestAsset.duration = [[notification object] duration];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMovieDurationAvailableNotification object:self.moviePlayer];
}
What am I doing wrong? I figured if I were to set the object: parameter to the instance of the MPMoviePlayerController it would allow me to get only the event for that instance. However, it appears that Im only getting the last notification dispatched.
You can only have 1 active MPMoviePlayerController instance. You can create multiple but only 1 will work at a time.
See (about 2 screens down):
http://developer.apple.com/library/ios/#documentation/MediaPlayer/Reference/MPMoviePlayerController_Class/Reference/Reference.html
"Note: Although you can create multiple MPMoviePlayerController objects and present their views in your interface, only one movie player at a time can play its movie."