当您的应用程序打开并位于前台时是否显示股票 iOS 通知横幅?

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

当 Apple 的官方 iOS 消息应用程序打开并在前台时,来自其他联系人的新消息会触发股票,本机 iOS 通知警报横幅。见下图。

这在 App Store 的第 3 方应用程序中是否可行?当您的应用 打开并在前台时 and/or 为您的应用推送通知?

测试我的应用程序时,收到通知,但没有iOS显示警报UI

但是这种行为在 Apple 的官方消息应用程序中看到的:

Local and Remote Notification Programming Guide 说:

When the operating system delivers a local notification or remote notification and the target app is not running in the foreground, it can present the notification to the user through an alert, icon badge number, or sound.

If the app is running in the foreground when the notification is delivered, the app delegate receives a local or remote notification.

所以是的,我们可以在前台接收通知数据。但我看不出 显示本机 iOS 通知警报 UI.

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
{
    // I know we still receive the notification `userInfo` payload in the foreground.
    // This question is about displaying the stock iOS notification alert UI.

    // Yes, one *could* use a 3rd party toast alert framework. 
    [self use3rdPartyToastAlertFrameworkFromGithub]
}

Messages 是否会使用私有 API 在前台显示提醒?

就此问题而言,请不要在 github 等上建议任何第 3 方“吐司”弹出警报。我只感兴趣如果可以使用 stock、native iOS 本地或推送通知警报 UI 当您的应用程序打开并在前台时.

您可以自己处理通知并显示自定义提醒。 Viber、Whatsapp 和 BisPhone 等应用程序使用这种方法。

第 3 方自定义警报的一个示例是 CRToast

尝试在您的应用程序处于前台时安排本地通知,您会发现没有显示股票 iOS 提醒:

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];
}

编辑:

现在可以在 iOS10 中使用前台警报! .

iOS 9 及以下:

当您的应用程序打开并位于前台时,似乎无法显示库存 iOS 通知提醒。 Messages.app 必须使用私有 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

UIApplicationDelegate 方法 仍然被调用,允许您的应用响应本地或远程通知:

application:didReceiveLocalNotification:
application:didReceiveRemoteNotification:

但是,股票本机 iOS 通知警报横幅 UI 将 不会显示 ,因为它在 Apple 的 Messages.app 中必须正在使用 Private API.

您最好的办法是滚动自己的警报横幅或使用现有框架:

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

我已经在此处打开了此行为的雷达:rdar://22313177

要在应用程序打开时显示通知,我们需要手动处理。所以我在下面做的是在收到通知后进行处理。

在AppDelegate.m

中添加以下所有内容
  1. 处理通知呼叫
  2. 创建一个视图,添加AppIcon,Notification 消息并显示为动画
  3. 添加触摸识别器以在触摸时移除或在 5 秒内移除并带有动画。

让我知道这是否是一个好的解决方案。对我来说效果很好,但不确定这是否正确。

- (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) {


}];


}

iOS 10 添加了 UNUserNotificationCenterDelegate 协议,用于在您的应用程序处于前台时处理通知。

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;

UNNotificationPresentationOptions 标志允许您指定 UNNotificationPresentationOptionAlert 使用通知提供的文本显示警报。

这是关键,因为它允许您在应用程序打开并在前台时显示警报,这是 iOS 10.[=19 的新功能=]


示例代码:

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // Set UNUserNotificationCenterDelegate
        UNUserNotificationCenter.current().delegate = self
        
        return true
    }
    
}

// Conform to UNUserNotificationCenterDelegate
extension AppDelegate: UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(_ center: UNUserNotificationCenter,
           willPresent notification: UNNotification,
           withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        completionHandler(.alert)
    }
    
}

要显示本地通知,这是最佳选择。需要更少的代码来编写 "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)
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);
}];

我可以在 iOS 10.

的应用程序处于活动状态时显示推送通知
  1. 来自服务器的推送通知应该静音

  2. 当从服务器接收到远程通知时,您发送本地通知并设置 keyPath 的值: shouldAlwaysAlertWhileAppIsForeground = True

要在应用程序处于前台时显示横幅消息,请使用以下方法。

iOS 10+, Swift 3+:

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

这是应用在前台或打开阶段时接收推送通知的代码,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])
}

如果您需要访问通知的用户信息使用代码:notification.request.content.userInfo

方法 userNotificationCenter(_:willPresent:withCompletionHandler:) 仅在您向负载添加属性 content-available:1 时才会调用。最终的有效载荷应该是这样的:

{
     "aps":{
          "alert":"Testing.. (7)",
          "badge":1,"sound":"default"
     },
     "content-available":1
}

Swift 3 版本

当应用程序在前台时,这会显示警告。

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)")
    }
}

A​​pplicationDelegate 的实现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
}

如果您的部署目标 >= iOS10,请使用如下所示的 UNUserNotification-

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        // Change this to your preferred presentation option
        completionHandler([.alert, .sound])
    }

这里是一个适用于 SwiftUI 的版本。像这样修改您的主应用程序文件:

import SwiftUI
import UserNotifications

let appDelegate = AppDelegate()

@main
struct MyApp: App {
    let persistenceController = PersistenceController.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear() {
                    UNUserNotificationCenter.current().delegate = appDelegate
                }
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}

class AppDelegate: UIResponder, UIApplicationDelegate {
    
}

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