I have multiple nsnotificationcenters running at one time that display an alert when triggered. At times, this causes more than one alert to fire at a time but of course you can only display one and the other does not appear. What is the best way handle this situation so multiple alerts can go in succession.
I have tired having the alerts in one method and when one alert is showing, put another notification in an array and then run through that array but that is not working correctly. I have also tried to have the alerts in separate methods but that is not working either.
I have looked at using semaphores but could not find a good example.
This is my notifications which works as expected. I was looking for some advice on the notifications also. Where would be the best place to add the observer, in the viewDidAppear of viewDidLoad. viewDidLoad give a warning whenever an alert is displayed because it wants to display on a view that is not in the hierarchy.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// * 26 APR 2019 * 1.0.4.0
// Add observer for notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"ROC" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"ROP" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"CARGO" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"PAX" object:nil];
}
This is my selector method using a single method for all of the alerts. I am new to coding so I am sure this is not good practice so any advice would be appreciated. I am trying to put any additional notifications in an array if the current view is a uialertcontroller and then run through the array and display those alerts after but that is not working how i would like it to.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// * 26 APR 2019 * 1.0.4.0
// Add observer for notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"ROC" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"ROP" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"CARGO" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"PAX" object:nil];
}
- (void)receivedNotification:(NSNotification *)notification {
NSMutableDictionary *msgData = [[FlightDataInput sharedFlightDataInput] dataForPage:4];
NSMutableArray *alertArray = [[NSMutableArray alloc] init];
if([self.presentedViewController isKindOfClass:[UIAlertController class]]) {
[alertArray addObject:notification];
}
if(![self.presentedViewController isKindOfClass:[UIAlertController class]] && [alertArray count] == 0) {
if([notification.name isEqualToString: #"ROC"]) {
UIAlertController *alertRoc = [UIAlertController alertControllerWithTitle:[msgData valueForKey:#"rocTitle"] message:[msgData valueForKey:#"rocMsg"] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[alertRoc dismissViewControllerAnimated:YES completion:nil];
}];
[alertRoc addAction:ok];
[self presentViewController:alertRoc animated:NO completion:nil];
}
if ([notification.name isEqualToString:#"ROP"]) {
UIAlertController *alertRop = [UIAlertController alertControllerWithTitle:[msgData valueForKey:#"ropTitle"] message:[msgData valueForKey:#"ropMsg"] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[alertRop dismissViewControllerAnimated:YES completion:nil];
}];
[alertRop addAction:ok];
[self presentViewController:alertRop animated:NO completion:nil];
}
if ([notification.name isEqualToString:#"CARGO"]) {
UIAlertController *alertCargo = [UIAlertController alertControllerWithTitle:[msgData valueForKey:#"cargoTitle"] message:[msgData valueForKey:#"cargoMsg"] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[alertCargo dismissViewControllerAnimated:YES completion:nil];
}];
[alertCargo addAction:ok];
[self presentViewController:alertCargo animated:NO completion:nil];
}
if ([notification.name isEqualToString:#"PAX"]) {
UIAlertController *alertPax = [UIAlertController alertControllerWithTitle:[msgData valueForKey:#"paxTitle"] message:[msgData valueForKey:#"paxMsg"] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[alertPax dismissViewControllerAnimated:YES completion:nil];
}];
[alertPax addAction:ok];
[self presentViewController:alertPax animated:NO completion:nil];
}
}
if([alertArray count] > 0) {
for(int i = 0; i < [alertArray count]; i++) {
// creating the same alerts in here if there are alerts in the array
}
}
}
I have multiple nsnotificationcenters running at one time that display an alert when triggered. At times, this causes more than one alert to fire at a time but of course you can only display one and the other does not appear. What is the best way handle this situation so multiple alerts can go in succession.
I have tired having the alerts in one method and when one alert is showing, put another notification in an array and then run through that array but that is not working correctly. I have also tried to have the alerts in separate methods but that is not working either.
Hi you can present only one Alert at a time. if you want more in chain so first dismiss exist Alerts. Here is Sample please check and update.
{
NSMutableDictionary *msgData;
NSMutableArray <NSNotification *> *alertArray;
int alertIndex;
}
- (void)viewDidLoad:(BOOL)animated {
[super viewDidLoad:animated];
// * 26 APR 2019 * 1.0.4.0
// Add observer for notifications
msgData = [[FlightDataInput sharedFlightDataInput] dataForPage:4];
alertArray = [NSMutableArray new];
alertIndex = 0;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"ROC" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"ROP" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"CARGO" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedNotification:) name:#"PAX" object:nil];
}
- (void)receivedNotification:(NSNotification *)notification {
[alertArray addObject:notification];
if(![self isAlertExist]) {
[self checkAlerts];
}
}
-(void) checkAlerts
{
if(alertIndex < [alertArray count])
{
NSNotification *notification = (NSNotification *)[alertArray objectAtIndex:arrayIndex];
arrayIndex = arrayIndex + 1;
if([notification.name isEqualToString: #"ROC"]) {
[self showAlertWithTitle:[msgData valueForKey:#"rocTitle"] andMessage:[msgData valueForKey:#"rocMsg"]];
}
else if ([notification.name isEqualToString:#"ROP"]) {
[self showAlertWithTitle:[msgData valueForKey:#"ropTitle"] andMessage:[msgData valueForKey:#"ropMsg"]];
}
else if ([notification.name isEqualToString:#"CARGO"]) {
[self showAlertWithTitle:[msgData valueForKey:#"cargoTitle"] andMessage:[msgData valueForKey:#"cargoMsg"]];
}
else if ([notification.name isEqualToString:#"PAX"]) {
[self showAlertWithTitle:[msgData valueForKey:#"paxTitle"] andMessage:[msgData valueForKey:#"paxMsg"]];
}
}
}
-(void) showAlertWithTitle:(NSString *)title andMessage:(NSString *)message
{
UIAlertController *alertPax = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[alertPax dismissViewControllerAnimated:YES completion:^{
[self checkAlerts];
}];
}];
[alertPax addAction:ok];
[self presentViewController:alertPax animated:NO completion:nil];
}
-(BOOL) isAlertExist {
for (UIWindow* window in [UIApplication sharedApplication].windows) {
if ([window.rootViewController.presentedViewController isKindOfClass:[UIAlertController class]]) {
return YES;
}
}
return NO;
}
Related
I am displaying a UIAlertController when I receive a response from API. After the response, I want to call the notification of other view controller.
I added observer in view controller 1 as below:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(UpdateAthleteDictionaryNotification:) name:#"UpdateAthleteDictionary" object:self.dictProfile];
In view controller 2 my code is below:
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:applicationName
message:[jsonObject valueForKey:#"responseMessage"]
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* yesButton = [UIAlertAction
actionWithTitle:#"Ok"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdateAthleteDictionary" object:self.dictProfile];
});
[self.navigationController popViewControllerAnimated:YES];
}];
[alert addAction:yesButton];
[self presentViewController:alert animated:YES completion:nil];
But it does not call. So please can you suggest me the solutions.
NSNotificationCenter will deliver your notification only in case if name and object are the same (source).
If you want to receive notifications with any object attached you should pass nil as an object argument, in your case:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(UpdateAthleteDictionaryNotification:) name:#"UpdateAthleteDictionary" object:nil];
This is my AlertView code :
- (void)initializeAlertControllerForOneButtonWithTitle:(NSString *)title withMessage:(NSString *)msg withYesButtonTitle:(NSString *)yesButtonTitle withYesButtonAction:(id)yesButtonAction
{
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:title
message:msg
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* yesBtn = [UIAlertAction
actionWithTitle:yesButtonTitle
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (self.activityIndicator.animating) {
[self.activityIndicator stopAnimating];
}
if ([title isEqualToString:#"Wrong Password!"]) {
self.editTextField.text = #"";
[self.editTextField becomeFirstResponder];
}
}];
[alert addAction:yesBtn];
[self presentViewController:alert animated:YES completion:nil];
}
I am trying to fire this alert in my NSNotificatoin Response method. My Notification Response code :
- (void)receiveSMSVerificationResponse:(NSNotification *)notification
{
SMSVerificationDigitClassModel *smsVerificationDigitClassModel = [[SMSVerificationDigitClassModel alloc] init];
smsVerificationDigitClassModel = [notification object];
if (smsVerificationDigitClassModel.viewControllerName == ViewControllerNameProfileInfoEditViewController) {
if ([self alreadyRegisteredPhoneNumber:smsVerificationDigitClassModel.phoneNumber] == YES) {
NSLog(#"jogajog");
[self initializeAlertControllerForOneButtonWithTitle:#"Already Registered!" withMessage:kAlreadyRegisteredPhoneNumberMSGForChangePhoneNumber withYesButtonTitle:#"Ok" withYesButtonAction:nil];
} else {
if ([AdditionalClasses internetConnectionCheck] == YES) {
self.userModelClass.phone_number = smsVerificationDigitClassModel.phoneNumber;
[self updateUserModel:self.userModelClass];
} else {
[self noInternetConnectionAlert];
}
}
//Check if that phone number is already used
// udate phone numner in server
// update phone number in core data
//[self goToSignUpViewControllerWithPhoneNumber:smsVerificationDigitClassModel.phoneNumber];
}
}
I check it from break point, this line [self initializeAlertControllerForOneButtonWithTitle:#"Already Registered!" withMessage:kAlreadyRegisteredPhoneNumberMSGForChangePhoneNumber withYesButtonTitle:#"Ok" withYesButtonAction:nil]; is actually calling, but the alert view is not popping up. It says:
"Warning: Attempt to present on whose view is not in the window hierarchy!"
I have tried to add notification observer methods :
- (void)addNotificationObserver
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveUserModelResponse:) name:#"sendUpdateRequestToServerForPhoneNumberWithUserModel" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveSMSVerificationResponse:) name:#"SMSVerificationForPhoneNumber" object:nil];
}
In viewDidLoad, viewDidAppear & in viewWillAppear and removeObserver in dealloc,
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"sendUpdateRequestToServerForPhoneNumberWithUserModel" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"SMSVerificationForPhoneNumber" object:nil];
}
but it is not showing at all. So, how do I change my window hierarchy! in this viewController. If you understand, please reply back. A lot of thanks in advance.
Call the initializeAlertControllerForOneButtonWithTitle method inside a main queue dispatch queue block.
All UI operation should be on main threat.
dispatch_async(dispatch_get_main_queue(), ^{
[self initializeAlertControllerForOneButtonWithTitle:#"Already Registered!" withMessage:kAlreadyRegisteredPhoneNumberMSGForChangePhoneNumber withYesButtonTitle:#"Ok" withYesButtonAction:nil];
});
In this code the alert action is shown every time the app become active:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Please make your choice"
message:#"Would you like a cup of coffee?"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *yesAction = [UIAlertAction actionWithTitle:#"YES"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"You tapped YES");
}];
UIAlertAction *maybeAction = [UIAlertAction actionWithTitle:#"MAYBE"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"You tapped MAYBE");
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:nil];
[alertController addAction:yesAction];
[alertController addAction:maybeAction];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
Also, everything works as expected if I move the code block of the UIAlertController in viewDidAppear method.
But if I move the UIAlertController in viewDidLoad :
- (void)viewDidLoad {
UIAlertController *alertController [...]
[...]
[self presentViewController:alertController animated:YES completion:nil];
}
it doesn't works. The alert is not shown.
In viewDidLoad it is not part of the view hierarchy at that time hence it is silently ignored, on viewWillAppear at this time the view hierarchy is already set up hence the reason it works.
I'm trying to get a movie that plays to dismiss on its own without having to hit the "Done" button. I think it's a recent iOS 6 problem since I'm following a tutorial exactly and when I insert NSLogs the NSNotification and removeFromSuperview are being recognized but the movie stays there once it has ended. Here's my code, please help:
- (IBAction)playMovie:(id)sender
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"RomneyFlipSequence1" ofType:#"mov"]];
_moviePlayer =
[[MPMoviePlayerController alloc]
initWithContentURL:url];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:_moviePlayer];
_moviePlayer.controlStyle = MPMovieControlStyleDefault;
_moviePlayer.shouldAutoplay = YES;
[self.view addSubview:_moviePlayer.view];
[_moviePlayer setFullscreen:YES animated:NO];
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
if ([player
respondsToSelector:#selector(setFullscreen:animated:)])
{
[player.view removeFromSuperview];
}
}
#end
You entered the player into fullscreen mode.
[_moviePlayer setFullscreen:YES animated:NO];
Reading other solutions in SO it seems that when you press "Done" the player is taken out of fullscreen first, and then the notification is thrown. After reading this answer, adding
[_moviePlayer setFullscreen:NO animated:YES];
before your removeFromSuperview call will solve your problem.
If the above doesn't work, in addition you may try stoping the player first so the full code will be
-(void)removePlayer:(MPMoviePlayerController *)player{
NSLog(#"Playback Finished");
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerDidExitFullscreenNotification object:_moviePlayer];
[_moviePlayer stop]; // <-- May not be needed
if ([_moviePlayer respondsToSelector:#selector(setFullscreen:animated:)])
{
[_moviePlayer setFullscreen:NO animated:YES];
[_moviePlayer.view removeFromSuperview];
}
_moviePlayer=nil;
}
I use the same method for both notifications "Done" and "PlayBackFinished"
Hope this helps.
You had added _moviePlayer.view as a view and you are trying to remove player.view
i think this will do it :
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
if ([player
respondsToSelector:#selector(setFullscreen:animated:)])
{
[_moviePlayer.view removeFromSuperview];
}
}
When my app goes to background and back, modal view closes. Why is this happend? I tried with pause on and off. I have ARC enabled, if it is a useful info.
MPMoviePlayerViewController * player = [[MPMoviePlayerViewController alloc] initWithContentURL:targetURL];
[player.moviePlayer prepareToPlay];
[self presentMoviePlayerViewControllerAnimated:player];
In case anyone else stumbles across this (as I did before I found my own solution), I was able to prevent MPMoviePlayerViewController from exiting when the app backgrounds by subclassing it and removing it as an observer of UIApplicationDidEnterBackgroundNotification:
- (id)initWithContentURL:(NSURL *)contentURL {
self = [super initWithContentURL:contentURL];
if (self){
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
return self;
}
You don't need to subclass MPMoviePlayerViewController - just do following after creating MPMoviePlayerViewController instance.
[[NSNotificationCenter defaultCenter] removeObserver:self.moviePlayerViewController name:UIApplicationDidEnterBackgroundNotification object:nil];
Here is full code that can help you
// Call this to start initialization and play movie
-(void)prepareMoviePlayer:(NSURL *)moviePath
{
moviePlayerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:moviePath];
if ([[moviePlayerViewController moviePlayer] respondsToSelector:#selector(loadState)])
{
[[moviePlayerViewController moviePlayer] setControlStyle:MPMovieControlStyleNone];
[[moviePlayerViewController moviePlayer] setFullscreen:YES];
[[moviePlayerViewController moviePlayer] prepareToPlay];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
}
- (void) addPlayer
{
[[[moviePlayerViewController moviePlayer] view] setFrame:self.view.bounds];
[[self view] addSubview:[[moviePlayerViewController moviePlayer] view]];
}
static NSTimeInterval t;
// Call this on applicationWillResignActive
-(void) pauseMovieInBackGround
{
[[moviePlayerViewController moviePlayer] pause];
t = [[moviePlayerViewController moviePlayer] currentPlaybackTime];
[moviePlayerViewController.view removeFromSuperview];
}
// Call this on applicationWillEnterForeground
-(void) resumeMovieInFrontGround
{
[self addPlayer];
[[moviePlayerViewController moviePlayer] stop];
[[moviePlayerViewController moviePlayer] setInitialPlaybackTime:t];
[[moviePlayerViewController moviePlayer] play];
}
-(void)moviePlayerLoadStateDidChange:(NSNotification *)notification
{
if([[moviePlayerViewController moviePlayer] loadState] != MPMovieLoadStateUnknown)
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
[self addPlayer];
[[moviePlayerViewController moviePlayer] play];
}
}
-(void)videoPlayBackDidFinish:(NSNotification*)notification
{
[self dismissMoviePlayerViewControllerAnimated];
}
I am coming across this issue.
I've tried ChrisH's answer, but it didn't work for me until I put removeObserver into viewDidLoad() instead of init().
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
Then it works. Enjoy it.