Displaying a stock iOS notification banner when your app is open and in the foreground?

痞子三分冷 提交于 2019-11-26 14:09:42

iOS 10 adds the UNUserNotificationCenterDelegate protocol for handling notifications while your app is in the foreground.

The UNUserNotificationCenterDelegate protocol defines methods for receiving notifications and for handling actions. When your app is in the foreground, arriving notifications are delivered to your delegate object instead of displayed automatically using the system interfaces.

Swift:

optional func userNotificationCenter(_ center: UNUserNotificationCenter, 
                     willPresent notification: UNNotification, 
      withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void)

Objective-C:

- (void)userNotificationCenter:(UNUserNotificationCenter *)center 
       willPresentNotification:(UNNotification *)notification 
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler;

The UNNotificationPresentationOptions flags allow you to specify UNNotificationPresentationOptionAlert to display an alert using the text provided by the notification.

This is key as it allows you to display the alert while your app is open and in the foreground, which is new for iOS 10.

Sample code:

func userNotificationCenter(_ center: UNUserNotificationCenter,
       willPresent notification: UNNotification, 
       withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 
{
    completionHandler(.alert)
}
chengsam

For displaying banner message while app is in foreground, use the following method.

iOS 10, Swift 3:

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .badge, .sound])
}

EDIT:

Foreground alerts are now possible in iOS 10! Please see this answer.

For iOS 9 and below:

It does not seem to be possible to show the stock iOS notification alert when your app is open and in the foreground. Messages.app must be using a private API.

The system does not display any alerts, badge the app’s icon, or play any sounds when the app is already frontmost. - UILocalNotification docs

The UIApplicationDelegate methods will still be called, allowing your app to respond to the local or remote notification:

application:didReceiveLocalNotification:
application:didReceiveRemoteNotification:

However, the stock native iOS notification alert banner UI will not be shown as it is in Apple's Messages.app, which must be using a Private API.

The best you can do is is roll your own alert banner or use an existing framework:

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
{    
    // Use a 3rd party toast alert framework to display a banner 
    [self toastAlertFromGithub]
}

I have opened a radar for this behavior here: rdar://22313177

To show notifications while the App is open, we need to handle it manually. So what I am doing below is to handle the notification once received.

Add all below in AppDelegate.m

  1. Handle call for notify
  2. Create a view, add AppIcon, Notification message and show it as an animation
  3. Add Touch recogniser to remove if touched or remove in 5 seconds with animation.

Let me know if this is an ok solution. Worked well for me but am unsure if this is the right way.

- (void)application:(UIApplication *)applicationdidReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {


NSString *notifMessage = [[userInfo objectForKey:@"aps"] objectForKey:@"alert"];

//Define notifView as UIView in the header file
[_notifView removeFromSuperview]; //If already existing

_notifView = [[UIView alloc] initWithFrame:CGRectMake(0, -70, self.window.frame.size.width, 80)];
[_notifView setBackgroundColor:[UIColor blackColor]];

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10,15,30,30)];
imageView.image = [UIImage imageNamed:@"AppLogo.png"];

UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 15, self.window.frame.size.width - 100 , 30)];
myLabel.font = [UIFont fontWithName:@"Helvetica" size:10.0];
myLabel.text = notifMessage;

[myLabel setTextColor:[UIColor whiteColor]];
[myLabel setNumberOfLines:0];

[_notifView setAlpha:0.95];

//The Icon
[_notifView addSubview:imageView];

//The Text
[_notifView addSubview:myLabel];

//The View
[self.window addSubview:_notifView];

UITapGestureRecognizer *tapToDismissNotif = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                    action:@selector(dismissNotifFromScreen)];
tapToDismissNotif.numberOfTapsRequired = 1;
tapToDismissNotif.numberOfTouchesRequired = 1;

[_notifView addGestureRecognizer:tapToDismissNotif];


[UIView animateWithDuration:1.0 delay:.1 usingSpringWithDamping:0.5 initialSpringVelocity:0.1 options:UIViewAnimationOptionCurveEaseIn animations:^{

    [_notifView setFrame:CGRectMake(0, 0, self.window.frame.size.width, 60)];

} completion:^(BOOL finished) {


}];


//Remove from top view after 5 seconds
[self performSelector:@selector(dismissNotifFromScreen) withObject:nil afterDelay:5.0];


return;


}

//If the user touches the view or to remove from view after 5 seconds
- (void)dismissNotifFromScreen{

[UIView animateWithDuration:1.0 delay:.1 usingSpringWithDamping:0.5 initialSpringVelocity:0.1 options:UIViewAnimationOptionCurveEaseIn animations:^{

    [_notifView setFrame:CGRectMake(0, -70, self.window.frame.size.width, 60)];

} completion:^(BOOL finished) {


}];


}
Kavin Kumar Arumugam

Here is the code to receive Push Notification when app in foreground or in open stage, iOS 10 & Swift 2.3

@available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void)
{
     completionHandler([UNNotificationPresentationOptions.Alert,UNNotificationPresentationOptions.Sound,UNNotificationPresentationOptions.Badge])
}

If you need to access userInfo of notification use code: notification.request.content.userInfo

The method userNotificationCenter(_:willPresent:withCompletionHandler:) is only called if you add to payload the attribute content-available:1. The final payload should be something like:

{
     "aps":{
          "alert":"Testing.. (7)",
          "badge":1,"sound":"default"
     },
     "content-available":1
}
Nguyen Loc
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.body = body;
content.userInfo = userInfo;
content.sound = [UNNotificationSound defaultSound];
[content setValue:@(YES) forKeyPath:@"shouldAlwaysAlertWhileAppIsForeground"];

UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"Notif" content:content trigger:nil];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    DLog(@"Error:%@", error);
}];

I can show push notification when app is active for iOS 10.

  1. The push notification from server should be silent.

  2. When receive remote notification from server, you send a local notification and set the value for keyPath: shouldAlwaysAlertWhileAppIsForeground = True

Mo Farhand

You can handle the notification yourself and show a custom alert. Apps such as Viber, Whatsapp, and BisPhone use this approach.

One example of a 3rd party custom alert is CRToast.

Try scheduling a local notification while your app is in the foreground, and you will see that no stock iOS alert is shown:

if (application.applicationState == UIApplicationStateActive ) {
     UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.userInfo = userInfo;
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    localNotification.alertBody = message;
    localNotification.fireDate = [NSDate date];
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
Leslie Godwin

Swift 3 version

This shows an alert when the application is in the foreground.

if #available(iOS 10.0, *)
{
    // need to setup the global notification delegate somewhere when your app starts
    //
    UNUserNotificationCenter.current().delegate = applicationDelegate

    // to show a message
    //
    let content = UNMutableNotificationContent()
    content.body = "MESSAGE"

    let request = UNNotificationRequest(identifier: "fred", content: content, trigger: nil)
    UNUserNotificationCenter.current().add(request)
    {
       error in // called when message has been sent

       debugPrint("Error: \(error)")
    }
}

ApplicationDelegate's implementation of UNUserNotificationCenterDelegate

@available(iOS 10.0, *)
public func userNotificationCenter(_ center : UNUserNotificationCenter, willPresent notification : UNNotification, withCompletionHandler completionHandler : @escaping (UNNotificationPresentationOptions) -> Void)
{
    completionHandler([.alert]) // only-always show the alert
}

To show local Notification this is the best option . need less code to wright "BRYXBanner " https://cocoapods.org/pods/BRYXBanner

let banner = Banner(title: "title", subtitle: "subtitle", image: UIImage(named: "addContact"), backgroundColor: UIColor(red:137.0/255.0, green:172.0/255.0, blue:2.0/255.0, alpha:1.000))
    banner.dismissesOnTap = true
    banner.show(duration: 1.0)

If your deployment target >= iOS10, use UNUserNotification like below-

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        // Change this to your preferred presentation option
        completionHandler([.alert, .sound])
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!