Отображение стандартного баннера уведомлений iOS, когда ваше приложение открыто и находится на переднем плане?

123

Когда официальное приложение Apple для сообщений iOS открыто и отображается на переднем плане, новые сообщения от других контактов запускают стандартный баннер уведомления iOS. См. Изображение ниже.

Возможно ли это в сторонних приложениях в App Store? Локальные и / или Push-уведомления для вашего приложения, когда оно открыто и находится на переднем плане ?

При тестировании моего приложения уведомления приходят, но пользовательский интерфейс предупреждений iOS не отображается .

Но такое поведение будет увидеть в официальном приложении Сообщения Apple:

Сообщения открыты и на переднем плане.  По-прежнему показывает уведомление.

В Руководстве по программированию локальных и удаленных уведомлений говорится:

Когда операционная система доставляет локальное уведомление или удаленное уведомление, а целевое приложение не работает на переднем плане , оно может представить уведомление пользователю посредством предупреждения , номера значка или звука.

Если приложение работает на переднем плане, когда уведомление доставляется, делегат приложения получает локальное или удаленное уведомление.

Итак, да, мы можем получать данные уведомления, находясь на переднем плане. Но я не вижу возможности представить собственный пользовательский интерфейс уведомлений iOS .

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

Использует ли Сообщения частный API для отображения предупреждения, находясь на переднем плане?

В целях этого вопроса, пожалуйста, не предлагайте никаких сторонних всплывающих уведомлений о всплывающих окнах на github и т. Д. Меня интересует только то, можно ли это сделать с помощью стандартного , встроенного iOS Local или пользовательского интерфейса уведомлений с push-уведомлениями, пока вы приложение открыто и на переднем плане .

pkamb
источник

Ответы:

152

В iOS 10 добавлен UNUserNotificationCenterDelegateпротокол для обработки уведомлений, когда ваше приложение находится на переднем плане.

UNUserNotificationCenterDelegateПротокол определяет методы для получения уведомлений и для обработки действий. Когда ваше приложение находится на переднем плане, поступающие уведомления доставляются вашему объекту-делегату, а не отображаются автоматически с использованием системных интерфейсов.

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.


Образец кода:

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)
    }
    
}
pkamb
источник
4
Не могли бы вы предоставить образец рабочего кода, в котором входящее уведомление переднего плана отображается системой, как если бы приложение находилось в фоновом режиме?
azmeuk
1
@azmeuk Проверьте мой ответ ...
chengsam
8
Не забудьте добавить UNUserNotificationCenter.current().delegate = selfв application(_:willFinishLaunchingWithOptions:)или application(_:didFinishLaunchingWithOptions:)метода
DENISS Федотову
3
Также убедитесь, что ваше устройство не находится в режиме «Не беспокоить», иначе уведомления будут отображаться в Центре уведомлений, но не будут отображаться на переднем плане над вашим приложением
Jadent
2
мои 2 цента: не пропустите: импортируйте UserNotifications. !
ingconti
95

Для отображения сообщения баннера, когда приложение находится на переднем плане, используйте следующий метод.

iOS 10+, Swift 3+ :

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .badge, .sound])
}
chengsam
источник
1
Вау, везде, где я читал, люди говорили, что это невозможно. Я так рад, что нашел этот ответ, он работает именно так, как я ожидал! Когда приложение запущено, push-уведомления теперь отображаются точно так же, как если бы приложение было в фоновом режиме. Спасибо!
Торре Ласли
1
Куда идет этот фрагмент? В appDelegate или еще где?
Йоав Р.
@YoavR. AppDelegate
chengsam
Я использовал тот же код. Но я не знаю, почему уведомление не отображается, когда приложение было на переднем плане.
Boominadha Prakash M
3
Chengsam, возможно, вы захотите исправить это: его не нужно вставлять в AppDelegate. Его нужно использовать для любого объекта, которому соответствует UNUserNotificationCenterDelegate. При этом большинство разработчиков стараются AppDelegateсоответствовать UNUserNotificationCenterDelegate. @YoavR.
мед
16

РЕДАКТИРОВАТЬ:

Предупреждения переднего плана теперь возможны в iOS 10! Пожалуйста, посмотрите этот ответ .

Для iOS 9 и ниже:

Кажется, что невозможно показать стандартное уведомление об уведомлении iOS, когда ваше приложение открыто и находится на переднем плане. Messages.app должен использовать частный API.

Система не отображает никаких предупреждений, не отображает значок приложения и не воспроизводит звуки, когда приложение уже находится на переднем плане. - Документы UILocalNotification

Эти UIApplicationDelegateметоды будут все еще можно назвать, что позволяет ваше приложение , чтобы ответить на локальном или удаленном уведомления:

application:didReceiveLocalNotification:
application:didReceiveRemoteNotification:

Однако штатный пользовательский интерфейс баннера уведомлений iOS не будет отображаться, как в Apple Messages.app, который должен использовать частный API.

Лучшее, что вы можете сделать, - это развернуть собственный баннер с предупреждениями или использовать существующую структуру:

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

Я открыл радар для этого поведения здесь: rdar://22313177

pkamb
источник
3
Как же тогда WhatsApp и другие известные приложения это делают?
Machado
@tardoandre есть скриншот? Я предполагаю, что это имитирует стандартное оповещение iOS в их собственных представлениях.
pkamb
2
Вы должны включить ссылку, toastAlertFromGithubесли вы предлагаете использовать эту стороннюю библиотеку!
Кетан Пармар
14

Чтобы отображать уведомления, пока приложение открыто, нам нужно обрабатывать их вручную. Итак, что я делаю ниже, так это обрабатывать полученное уведомление.

Добавьте все ниже в AppDelegate.m

  1. Обработать вызов для уведомления
  2. Создайте представление, добавьте AppIcon, сообщение уведомления и покажите его как анимацию
  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) {


}];


}
Хафиз
источник
3
Это может быть хорошим решением для создания собственного предупреждения, но он не показывает Stock IOS уведомление , как показано на скриншоте.
pkamb 07
Привет, Спасибо за добрые слова. Да, это не будет выглядеть так (если вы говорите о внешнем виде), поскольку показанное выше на изображении является уведомлением iOS, а сообщение через код - индивидуальным. Возможно, мы сможем воспроизвести образы в представлении. Я думаю, что у WhatsApp есть аккуратный вид с размытым фоном и т. Д.
Хафиз,
1
Кто-то проголосовал против :(. Было бы неплохо услышать, почему!?. Спасибо.
Hafeez
2
Я сделал, как код в этом ответе (приятно , хотя это может быть) на самом деле не ответить на вопрос , спросил: показывая акции IOS тоста баннер внутри приложения. Есть и другие вопросы по SO, где этот ответ был бы отличным.
pkamb
1
Спасибо pkamb за разъяснения, имеет смысл. Думаю, я пропустил слово "сток".
Hafeez
10

Вот код для получения push-уведомлений, когда приложение находится на переднем плане или на открытой стадии, 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])
}

Если вам нужно получить доступ к userInfo уведомления, используйте код: notification.request.content.userInfo

Метод userNotificationCenter(_:willPresent:withCompletionHandler:)вызывается только в том случае, если вы добавляете атрибут в полезную нагрузку content-available:1. Окончательная полезная нагрузка должна быть примерно такой:

{
     "aps":{
          "alert":"Testing.. (7)",
          "badge":1,"sound":"default"
     },
     "content-available":1
}
Кавин Кумар Арумугам
источник
1
userNotificationCenter(_:willPresent:withCompletionHandler:)будет вызываться, когда ваше приложение работает на переднем плане. Таким образом, это НЕ имеет значения для « доступное содержимое », которое связано с « фоновой выборкой ».
DawnSong 01
9
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);
}];

Я могу показывать push-уведомление, когда приложение активно для iOS 10.

  1. Push-уведомление с сервера должно быть безмолвным .

  2. При получении удаленного уведомления от сервера вы отправляете локальное уведомление и устанавливаете значение для keyPath: shouldAlwaysAlertWhileAppIsForeground = True

Нгуен Лок
источник
в swift 2 - да content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")? (с «Да» и неправда)
Йоав Р.
Внимание: это приведет к сбою вашего приложения под iOS 12. См. Обсуждение здесь: stackoverflow.com/questions/41373321/…
Мэтт Беарсон
5

Вы можете обработать уведомление самостоятельно и показать индивидуальное оповещение. Такой подход используют такие приложения, как Viber, Whatsapp и BisPhone.

Одним из примеров стороннего настраиваемого оповещения является 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];
}
Мо Фарханд
источник
Спасибо, но просто ищу ответы с помощью стандартного уведомления iOS.
pkamb
1
хорошо, но я думаю, что вы не можете этого сделать, также посмотрите эту ссылку: stackoverflow.com/questions/14872088/…
Mo Farhand
6
@matt Я пытаюсь сохранить этот вопрос в теме: показывать предупреждение iOS на переднем плане. Множество других вопросов о "лучшей структуре предупреждений о тостах". Ответ «Нет, вы не можете показывать предупреждение iOS на переднем плане» будет вполне приемлемым, и я приму этот ответ, пока это не станет возможным в будущей версии iOS.
pkamb
4

Версия 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)")
    }
}

Реализация ApplicationDelegate 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
}
Лесли Годвин
источник
просто чтобы быть уверенным ... вы имеете в виду, что с iOS 10 теперь вы также можете показывать предупреждения / баннеры / звуки на переднем плане, как и в фоновом режиме?
Дорогая,
Где я могу разместить указанную выше часть кода? во что получилRemoteNotification?
user1960169 01
@Leslie Godwin, будет ли он работать на ios 8.0 для отображения уведомлений на
Дилип Тивари
2

Это лучший вариант для отображения локального уведомления. нужно меньше кода, чтобы написать "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)
Мантош Кумар
источник
Эта BRYXBannerструктура , кажется, не использовать акции уведомления баннер IOS.
pkamb 06
1

Если ваша цель развертывания> = iOS10, используйте UNUserNotification, как показано ниже:

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        // Change this to your preferred presentation option
        completionHandler([.alert, .sound])
    }
Вишвас Сингх
источник