How to remove NSUserNotification - objective-c

I am using NSUserNotificationCenter to display scheduled Notifications. I have notifications of my app in the right side notification panel so whenever I click on the notification it'll launch the app but it won't remove notification from the panel.
When app is not running and I clicked on the notification, applicationDidFinishLaunching is called and it won't remove notification because didActivateNotification delegate is not called.
When application is already running and I clicked on the notification, appShouldHandleReopen is called. And won't call the delegate.
-(void)userNotificationCenter:(id)center didActivateNotification:(id)notification
I checked behavior in all the standard app they will remove notification when user click on the notifications.
I have implemented it as follows:
/* will create notification and set the delegate*/
#interface NotificationViewController : NSViewController
{
id delegate;
}
-(void)showNotification;
#end
#implementation NotificationViewController
- (void)createNotification:(NSString*)str
{
Class UserNotificationClass=NSClassFromString(#"NSUserNotification");
id notification = [[UserNotificationClass alloc] init];
//Set the title of the notification
[notification setTitle:#"My Notification"];
[notification setSubtitle:#"Test"];
[notification setHasActionButton:YES];
[notification setActionButtonTitle:#"OK"];
[notification setOtherButtonTitle:#"CANCEL"];
//Set the text of the notification
[notification setInformativeText:str];
[notification setDeliveryDate:[NSDate dateWithTimeInterval:0.1 sinceDate:[NSDate date]]];
[notification setSoundName:#"NSUserNotificationDefaultSoundName"];
[notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:str,#"string",nil]];
Class UserNotificationCenterClass=NSClassFromString(#"NSUserNotificationCenter");
id center = [UserNotificationCenterClass defaultUserNotificationCenter];
[center setDelegate:self.delegate];
//Scheldule our NSUserNotification
[center scheduleNotification:notification];
[notification release];
}
#end
/* Class which implements NSUserNotificationDelegate*/
#interface NotificationHandler : NSObject <NSUserNotificationCenterDelegate>
{
}
- (void)createNotification:(NSString*)str;
#end
#implementation NotificationHandler
- (void)createNotification:(NSString*)str
{
NotificationHandler *notificationHandler = [[NotificationHandler alloc] initWithNibName:#"NotificationHandler" bundle:[NSBundle mainBundle]];
notificationHandler.delegate = self;
[notificationHandler showNotification];
}
- (void)userNotificationCenter:(id)center didActivateNotification:(id)notification
{
[center removeDeliveredNotification:notification];
}
- (BOOL)userNotificationCenter:(id)center shouldPresentNotification:(id)notification
{
return YES;
}
#end
However this is not working in my case. Please let me know some pointer on it...
Other thing I observed:
When I implement NSUserNotificationDelegate into AppDelegate it'll work for 2nd case i.e. (When application is already running and I clicked on the notification) - in this case notification is removed.
And for deleting notification for 1st case i.e. (When app is not running and I clicked on the notification, appDidFinishLaunching is called) I wrote following code in appDidFinishLaunching.
NSUserNotification *userNoti = [[notification userInfo] valueForKey:#"NSApplicationLaunchUserNotificationKey"];
[[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:userNoti];
Can anybody let me know whats the reason, why this works in the AppDelegate but not in my above implementation or Am I doing something wrong in first one?
Is my AppDelegate implementation fine to handle my both scenarios?
Thanks in Advance.

Related

How to get iBeacon Notification when the app is in Background mode in ios 10 and above?

I have implemented iBeacon Notification in iOS 10 using objective-c. Can some one help me solve this problem to get the iBeacon in background mode in ios 10?
Even if the app is not running, location events (related to the beacons in this case) are handled the same way as any other app launching events. Every time a phone enters or exits a region while the app is terminated, it will be automatically launched.
application:didFinishLaunchingWithOptions: method (of AppDelegate class) is called with UIApplicationLaunchOptionsLocationKey key existing in launchOptions parameter.
When you verify this key exists (so location was the reason that your app was launched) you should create new instance of ESTBeaconManager class, set delegate to AppDelegate object (or any other object that is working as ESTBeaconManagerDelegate and was created before this event occurred) and start monitoring.
Region you are passing to the startMonitoringForRegion: method is not important, as ESTBeaconManager delegate will receive the most recent region information. You can just pick any of the ones your app registered in iOS. After Monitoring is revoked, app will automatically receive most recent entered/exited region event in beaconManager:didEnterRegion: or beaconManager:didExitRegion: method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if([launchOptions objectForKey:#"UIApplicationLaunchOptionsLocationKey"])
{
self.beaconManager = [ESTBeaconManager new];
self.beaconManager.delegate = self;
// don't forget the NSLocationAlwaysUsageDescription in your Info.plist
[self.beaconManager requestAlwaysAuthorization];
[self.beaconManager startMonitoringForRegion:[[ESTBeaconRegion alloc]
initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
identifier:#"AppRegion"]];
}
return YES;
}
-(void)beaconManager:(ESTBeaconManager *)manager didEnterRegion:(ESTBeaconRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Enter region";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
-(void)beaconManager:(ESTBeaconManager *)manager didExitRegion:(ESTBeaconRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Exit region";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

Detect click on context menu osx

How can I make sure a method gets called each time I expand a NSMenu. I have tried connecting the action from storyboard but the action only seem to get fired when i click menu items, not the menu itself.
[item setAction: #selector(play:)]
I would like to run a method when for instance the help menu gets expanded, to update the enabled and disabled content of that menu, since it is supposed to be different for logged in and not logged in users..
Update:
I added the NSMenuDelegate protocol in brackets in my
#interface ClientAppDelegate : NSObject<NSApplicationDelegate,NSMenuDelegate>
Adding menu items work but the menu does not seem to affect the delegate methods.
// Create the application on the UI thread.
- (void)createApplication:(id)object {
NSApplication* application = [NSApplication sharedApplication];
[NSBundle loadNibNamed:#"MainMenu" owner:NSApp];
// Set the delegate for application events.
[application setDelegate:self];
// Add the Tests menu.
NSMenu* menubar = [application mainMenu];
[menubar setDelegate:self];
// TIDAL Create Controlls
NSMenuItem *controlsItem = [[[NSMenuItem alloc] initWithTitle:#"Controls"
action:nil
keyEquivalent:#""] autorelease];
NSMenu *controlsMenu = [[[NSMenu alloc] initWithTitle:#"Controls"] autorelease];
AddMenuItem(controlsMenu, #"Pl", ID_L_PL);
[controlsItem setSubmenu:controlsMenu];
[menubar addItem:controlsItem];
......
-(void) menuWillOpen:(NSMenu *)menu{
wprintf(L"ITEM CLICK CAN I UPDATE MENU VISIBILITY HERE?");
}
-(void) menuNeedsUpdate:(NSMenu *)menu{
wprintf(L"ITEM CLICK CAN I UPDATE MENU VISIBILITY HERE?");
}
Update2:
So I started over, imaginging that the problem had something to do with references and the fact that i was developing in objective-c++. However i can not get it to work in just a minimal Obj-C example either, below is my code. The only callback that seems to work is the one that handles item clicks. May the problem be present due to the fact that I am using :
_menubar = [application mainMenu];
[_menubar setDelegate:self];
To get my menubar and setup my menu delegate?
//
// AppDelegate.m
// menuTest
//
// Created by David Karlsson on 27/02/15.
// Copyright (c) 2015 David Karlsson. All rights reserved.
//
#import "AppDelegate.h"
void AddMenuItem(NSMenu *menu, NSString* label, int idval) {
NSMenuItem* item = [menu addItemWithTitle:label
action:#selector(menuItemSelected:)
keyEquivalent:#""];
[item setTag:idval];
}
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) IBOutlet NSMenu * menubar;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSLog(#"Launched menu test");
// Insert code here to initialize your application
NSApplication* application = [NSApplication sharedApplication];
//[NSBundle loadNibNamed:#"MainMenu" owner:NSApp];
// Set the delegate for application events.
[application setDelegate:self];
// Add the Tests menu.
_menubar = [application mainMenu];
[_menubar setDelegate:self];
NSMenuItem *controlsItem = [[NSMenuItem alloc] initWithTitle:#"Controls"
action:nil
keyEquivalent:#""];
NSMenu *controlsMenu = [[NSMenu alloc] initWithTitle:#"Controls"];
AddMenuItem(controlsMenu, #"1", 123);
AddMenuItem(controlsMenu, #"2", 124);
AddMenuItem(controlsMenu, #"3", 154);
[controlsItem setSubmenu:controlsMenu];
[_menubar addItem:controlsItem];
}
-(void) menuWillOpen:(NSMenu *)menu{
NSLog(#"ITEM CLICK CAN I UPDATE MENU VISIBILITY HERE?");
}
-(void) menuNeedsUpdate:(NSMenu *)menu{
NSLog(#"ITEM CLICK CAN I UPDATE MENU VISIBILITY HERE?");
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
NSLog(#"Teardown menu test");
}
- (IBAction)menuItemSelected:(id)sender {
// Retrieve the active RootWindow.
NSWindow* key_window = [[NSApplication sharedApplication] keyWindow];
if (!key_window){
return;
}
NSLog(#"CLICK");
}
#end
Set the menu's delegate, then use this object to implement menuNeedsUpdate: from the NSMenuDelegate protocol. This method is called just before a menu is shown and is specifically provided to allow you to make changes to the menu in question before it arrives on screen.
You want to implement parts of the NSMenu Delegate protocol and you might need an NSTimer that runs in modes that work during menu tracking in order to do live updates while the menu is visible.

NSUserNotification makes sound but does not show

My NSUserNotification is being delivered, playing the given sound, but doesn't show visually on the screen if my app is not the top-most application.
If my application is in the background, the notification does not show. If my application is active, it shows. In both cases the sound plays.
Here is how I'm sending the notification:
NSUserNotification *notification = [[NSUserNotification alloc] init];
[notification setTitle: #"hi"];
[notification setSoundName: NSUserNotificationDefaultSoundName];
[notification setDeliveryDate: [NSDate dateWithTimeIntervalSinceNow: 3]];
[[NSUserNotificationCenter defaultUserNotificationCenter] scheduleNotification: notification];
And I am overriding shouldPresentNotification to always present it.
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
...
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
OSX 10.10.1 Yosemite
How do I make this notification always display, just like it always plays the sound?
I found two ways to make a notification always display:
Remove all previous notifications with
[[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications];
Or by changing the identifier
notification.identifier = #"com.yourcompany.yourapp.notificationidentifier";

going back to AppDelegate to recreate a uitabbarcontroller

I have an app that is based on a tab bar view with a welcome screen (that leads to either signin or sign up process). basically, if you are logged in - you go straight to the tabbar view and if not, you go to the welcome screen, where you can chose to either go to sign in or sign up. assuming that you go to either sign in or sign up, i would like the tab bar view to reappear, however, all the declarations are in the AppDelegate. how can I "go back" and call the tabbatcontroller? is the structure / flow of my classes correct at all?
so to repeat:
user signed in -> first view is tab bar view
user logged out -> welcome screen view --> sign in / up screen view --> tab bar view
what i am looking for is what do i need to write in this action method that is called once the user clicks on "sign in" in the sign in page:
-(IBAction)done:(id)sender {
?????
}
for reference, my appDelegate is:
if(user signed in)
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIViewController *viewController1 = [[FirstTab alloc] initWithNibName:#"FirstTab" bundle:NSBundle.mainBundle];
UIViewController *viewController2 = [[SecondTab alloc] initWithNibName:#"SecondTab" bundle:NSBundle.mainBundle];
UINavigationController *secondNavController = [[UINavigationController alloc]initWithRootViewController:viewController2];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, secondNavController, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
}
else
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
SigninTabBarTemplateViewController *landingPage = [[SigninTabBarTemplateViewController alloc] initWithNibName:#"SigninTabBarTemplateViewController" bundle:nil];
self.window.rootViewController = (UIViewController *) landingPage;
[self.window makeKeyAndVisible];
}
There are many options you can consider.
This can be easily achieved with the use of delegate. If you want to close the VC that you presented modally, give it a delegate property. The delegate will be sent a message when required, letting it dismiss the VC. A good way to go with delegate is to write a custom procotol.
For example :
// the delegate will conform to this protocol
#protocol SignInVCDelegate
// this method will be called when required
//
-(void)signInCompleted;
#end
Now, make the object you want conforms to that protocol, for example the app delegate.
// .h
#import "SignInVCDelegate.h"
#interface YourAppDelegate : NSObject <..., SignInDelegate> {
...
SignInVC *signIn;
...
}
-(void)signInCompleted;
#end
The implementation looks like this :
// .m
...
-(void)signInCompleted {
...
[signIn.view removeFromSuperview];
}
-(BOOL)applicationDidFinishLaunching {
if(!logged) {
...
[signIn setDelegate:self];
[self.tabBarController presentModalViewController:signIn
animated:YES];
}
}
Now give signInVC a delegate property, which will be set before being presented modally, and send the delegate a message when the sign in process is completed.
// in .h
#property(retain) id <SignInDelegate>delegate;
// in .m
#synthesize delegate;
-(IBAction)validateSignIn {
...
[delegate signInCompleted];
}
You can write any method you want, this example is simplist, and it is useful to give the delegate some informations. In this case for example you could pass a user name, or user id, or what ever you want.
Another simple option is using notifications. This option lets any object informed when something happen, as long as it register for it. Given the same objects as the previous example, the app delegate will register for the notification, while the sign in view controller will post it.
// in app delegate .m
-(BOOL)applicationDidFinishLaunching {
...
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(signInCompleted)
name:#"UserSignedInNotification"
object:nil];
}
// in SignInVC.m
-(IBAction)validateSignIn {
...
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UserSignedInNotification"
object:self];
}
More informations about delegates and notifications in Communicating with Objects.
You could try doing something like this in the method where you know the user has successfully logged in. (Assuming SignedInTabbarViewController is your TabBarController)
SignedInTabbarViewController *signedInTabbarViewController = [[SignedInTabbarViewController alloc] init];
id mainDelegate = [[UIApplication sharedApplication] delegate];
[self.navigationController.view removeFromSuperview];
if( [mainDelegate respondsToSelector:#selector(setViewController:)]) {
[mainDelegate setViewController:signedInTabbarViewController];
}
UIWindow *mainWindow = [mainDelegate window];
[mainWindow addSubview: signedInTabbarViewController.view];
[signedInTabbarViewController release];

iphone SDK: Not sure why I am not receiving UITextField events?

I have defined the controller to receive the events.
#interface salesViewController : UIViewController
<UITextFieldDelegate>{
However, none of my events are not firing.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
//this is not getting called
}
In Interface Builder I assigned the TextField delegate to the salesView.
What am I missing?
You have to set the delegate properly. You observe the protocol, but you need to do this:
#interface YourController : UIViewController<UITextFieldDelegate> {
IBOutlet UITextField* field;
}
#end
#implementation YourController
-(void)viewDidLoad
{
[field setDelegate:self];
}
And you will receive the events. Alternatively, you can set the delegate in Interface Builder as well, along with doing it programmatically in loadView, allocating the field and setting the delegate.
Additionally, try to use NSNotificationCenter as little as possible. Notifications are somewhat obsolete unless there isn't really a direct path between you and the object in question. Just a small comment on the answer above.
what are you trying to accomplish? textFieldDidBeginEditing is messaged whenever the user selects the text field. If you are trying to update a label or something as the user makes edits, you need to setup an observer w/ NSNotificationCenter and watch for the notification that is fired whenever this happens.If you take this approach, make sure to remove the observer once you are done with it
for example:
#pragma mark
#pragma mark -
#pragma mark Notification Observers
- (void)addObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:) name:#"UITextFieldTextDidChangeNotification" object:nil];
}
- (void)removeObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:nil];
}
if you need to keep tabs on multiple text fields, do something like this for your selector:
- (void)textFieldDidChange:(NSNotification*)aNotification {
UITextField *textField = (UITextField *)[aNotification object];
if([textField isEqual:usernameTextField])
{
[user setUsername:usernameTextField.text];
}
else if([textField isEqual:phoneNumberTextField])
{
[user setPhoneNumber:phoneNumberTextField.text];
}
}