Wyświetlać baner powiadomień systemu iOS, gdy Twoja aplikacja jest otwarta i na pierwszym planie?

123

Gdy oficjalna aplikacja Apple iOS Messages jest otwarta i na pierwszym planie, nowe wiadomości od innych kontaktów uruchamiają standardowy, natywny baner ostrzegawczy iOS. Zobacz obrazek poniżej.

Czy jest to możliwe w aplikacjach innych firm w App Store? Lokalne i / lub powiadomienia push dla Twojej aplikacji, gdy aplikacja jest otwarta i na pierwszym planie ?

Podczas testowania mojej aplikacji otrzymywane są powiadomienia, ale nie jest wyświetlany interfejs użytkownika alertów iOS .

Ale to zachowanie jest widoczne w oficjalnej aplikacji Wiadomości Apple:

Wiadomości są otwarte i na pierwszym planie.  Nadal pokazuje alert powiadomienia.

Lokalne i zdalne programowanie Powiadomienie przewodnik mówi:

Gdy system operacyjny dostarcza lokalne lub zdalne powiadomienie, a aplikacja docelowa nie działa na pierwszym planie , może przedstawić powiadomienie użytkownikowi za pośrednictwem alertu , numeru znaczka ikony lub dźwięku.

Jeśli aplikacja działa na pierwszym planie po dostarczeniu powiadomienia, delegat aplikacji otrzymuje powiadomienie lokalne lub zdalne.

Więc tak, możemy otrzymywać dane powiadomienia będąc na pierwszym planie. Ale nie widzę sposobu na zaprezentowanie natywnego interfejsu powiadomień systemu 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]
}

Czy Messages używa więc prywatnego interfejsu API do wyświetlania alertu na pierwszym planie?

W związku z tym pytaniem nie sugeruj żadnych wyskakujących alertów „toast” innych firm na githubie itp. Interesuje mnie tylko to, czy można to zrobić za pomocą podstawowego, natywnego interfejsu iOS Local lub Push Notification UI, gdy aplikacja jest otwarta i na pierwszym planie .

pkamb
źródło

Odpowiedzi:

152

iOS 10 dodaje UNUserNotificationCenterDelegateprotokół do obsługi powiadomień, gdy Twoja aplikacja jest na pierwszym planie.

UNUserNotificationCenterDelegateProtokół określa sposoby otrzymywania powiadomień oraz do przenoszenia działania. Gdy Twoja aplikacja jest na pierwszym planie, przychodzące powiadomienia są dostarczane do obiektu delegata zamiast automatycznie wyświetlać za pomocą interfejsów systemowych.

Szybki:

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

Cel C:

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

W UNNotificationPresentationOptions flagi pozwalają określić UNNotificationPresentationOptionAlertwyświetlanie alertu używając tekstu dostarczonego przez powiadomienia.

Jest to kluczowe, ponieważ umożliwia wyświetlanie alertu, gdy aplikacja jest otwarta i na pierwszym planie , co jest nowością w systemie iOS 10.


Przykładowy kod:

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
źródło
4
Czy możesz podać działający przykład kodu, w którym przychodzące powiadomienie na pierwszym planie jest wyświetlane przez system tak, jakby aplikacja działała w tle?
azmeuk
1
@azmeuk Sprawdź moją odpowiedź ...
chengsam
8
Nie zapomnij dodać UNUserNotificationCenter.current().delegate = selfw application(_:willFinishLaunchingWithOptions:)lub application(_:didFinishLaunchingWithOptions:)metody
Deniss Fedotovs
3
Upewnij się również, że nie masz swojego urządzenia w trybie Nie przeszkadzać, w przeciwnym razie powiadomienia będą widoczne w Centrum powiadomień, ale nie będą prezentowane na pierwszym planie nad Twoją aplikacją
Jadent
2
moje 2 centy: nie przegap: importuj UserNotifications. !
ingconti
95

Aby wyświetlić komunikat transparentu, gdy aplikacja jest na pierwszym planie, użyj następującej metody.

iOS 10+, Swift 3+ :

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .badge, .sound])
}
chengsam
źródło
1
Wow, wszędzie, gdzie czytałem, ludzie mówili, że to niemożliwe. Tak się cieszę, że znalazłem tę odpowiedź, działa dokładnie tak, jak się spodziewałem! Gdy aplikacja jest uruchomiona, powiadomienia push są teraz wyświetlane dokładnie tak, jak gdyby aplikacja działała w tle. Dziękuję Ci!
Torre Lasley
1
Gdzie idzie ten fragment? W aplikacji AppDelegate czy gdzieś indziej?
Yoav R.
@YoavR. AppDelegate
chengsam
Użyłem tego samego kodu. Ale nie wiem, dlaczego powiadomienie nie jest wyświetlane, gdy aplikacja była na pierwszym planie.
Boominadha Prakash M
3
Chengsam, możesz chcieć to poprawić: Nie trzeba go umieszczać w AppDelegate. Musi być nałożony na dowolny obiekt, z którym się zgadza UNUserNotificationCenterDelegate. Mając to na uwadze, większość programistów AppDelegatedostosowuje się do UNUserNotificationCenterDelegate. @YoavR.
Honey
16

EDYTOWAĆ:

Alerty na pierwszym planie są teraz możliwe w iOS 10! Zobacz tę odpowiedź .

IOS 9 i starsze:

Wydaje się, że nie jest możliwe wyświetlenie alertu powiadomienia systemu iOS, gdy aplikacja jest otwarta i znajduje się na pierwszym planie. Messages.app musi używać prywatnego interfejsu API.

System nie wyświetla żadnych alertów, nie odznacza ikony aplikacji ani nie odtwarza żadnych dźwięków, gdy aplikacja jest już na pierwszym planie. - UILocalNotification docs

Te UIApplicationDelegatemetody będą nadal nazywać, pozwalając swoją aplikację w odpowiedzi na lokalne lub zdalne powiadamianie:

application:didReceiveLocalNotification:
application:didReceiveRemoteNotification:

Jednak standardowy interfejs użytkownika banera powiadomień systemu iOS nie będzie wyświetlany tak, jak w aplikacji Apple Messages.app, która musi korzystać z prywatnego interfejsu API.

Najlepsze, co możesz zrobić, to rzucić własny baner ostrzegawczy lub użyć istniejącej struktury:

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

Otworzyłem tutaj radar dla tego zachowania: rdar://22313177

pkamb
źródło
3
Jak więc WhatsApp i inne znane aplikacje to robią?
Machado
@tardoandre masz zrzut ekranu? Zakładam, że dzieje się tak przez naśladowanie alertu na iOS w ich własnych widokach.
pkamb
2
Powinieneś dołączyć link do, toastAlertFromGithubjeśli sugerujesz użycie tej biblioteki innej firmy!
Ketan Parmar
14

Aby wyświetlać powiadomienia, gdy aplikacja jest otwarta, musimy obsługiwać ją ręcznie. Więc to, co robię poniżej, to obsłużyć otrzymane powiadomienie.

Dodaj wszystko poniżej w AppDelegate.m

  1. Obsługuj wezwanie do powiadomienia
  2. Utwórz widok, dodaj AppIcon, komunikat powiadomienia i pokaż go jako animację
  3. Dodaj rozpoznawanie dotyku, aby usunąć po dotknięciu lub usunąć w ciągu 5 sekund z animacją.

Daj mi znać, jeśli to dobre rozwiązanie. U mnie zadziałało, ale nie jestem pewien, czy to właściwy sposób.

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


}];


}
Hafeez
źródło
3
Może to być dobra odpowiedź do tworzenia własnego alertu, ale nie pokazuje alertu Stock iOS, jak pokazano na zrzucie ekranu.
pkamb,
Cześć, dziękuję za miłe słowa. Tak, nie będzie tak wyglądać (jeśli mówisz o wyglądzie), ponieważ ten pokazany powyżej na obrazku to powiadomienie iOS, a ten za pomocą kodu jest niestandardowy. Możemy odtworzyć wygląd w widoku. Myślę, że WhatsApp ma schludny wygląd z rozmyciem tła itp.
Hafeez,
1
Ktoś przegłosował :(. Byłoby miło usłyszeć dlaczego!?. Dzięki.
Hafeez
2
Zrobiłem, jak kod w tej odpowiedzi (miły choć może być) tak naprawdę nie odpowiedzieć na pytanie: pokazując seryjnych iOS tosty szyldem wewnątrz aplikacji. Są inne pytania dotyczące SO, na które ta odpowiedź byłaby świetna.
pkamb
1
Dzięki pkamb za wyjaśnienie, ma sens. Chyba przegapiłem słowo „zapasowe”.
Hafeez
10

Oto kod do otrzymywania powiadomień push, gdy aplikacja jest na pierwszym planie lub na otwartym etapie, iOS 10 i 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])
}

Jeśli chcesz uzyskać dostęp do informacji o użytkowniku powiadomienia, użyj kodu: notification.request.content.userInfo

Metoda userNotificationCenter(_:willPresent:withCompletionHandler:)jest wywoływana tylko wtedy, gdy dodasz atrybut do ładunku content-available:1. Ostateczny ładunek powinien wyglądać następująco:

{
     "aps":{
          "alert":"Testing.. (7)",
          "badge":1,"sound":"default"
     },
     "content-available":1
}
Kavin Kumar Arumugam
źródło
1
userNotificationCenter(_:willPresent:withCompletionHandler:)zostanie wywołana, gdy aplikacja będzie działać na pierwszym planie. Więc to NIE ma znaczenia przy „ dostępności treści ”, które jest związane z „ pobieraniem w tle ”.
DawnSong
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);
}];

Mogę wyświetlać powiadomienia push, gdy aplikacja jest aktywna na iOS 10.

  1. Powiadomienie push z serwera powinno być ciche .

  2. Otrzymując zdalne powiadomienie z serwera, wysyłasz powiadomienie lokalne i ustawiasz wartość dla keyPath: shouldAlwaysAlertWhileAppIsForeground = True

Nguyen Loc
źródło
w swift 2 - to content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")prawda? (z „Tak” i nieprawdą)
Yoav R.
Uwaga: spowoduje to awarię aplikacji w systemie iOS 12. Zobacz dyskusję tutaj: stackoverflow.com/questions/41373321/…
Matt Bearson
5

Możesz samodzielnie obsłużyć powiadomienie i wyświetlić niestandardowy alert. Aplikacje takie jak Viber, Whatsapp i BisPhone wykorzystują to podejście.

Przykładem alertu niestandardowego innej firmy jest CRToast .

Spróbuj zaplanować powiadomienie lokalne, gdy aplikacja jest na pierwszym planie, a zobaczysz, że nie jest wyświetlany żaden alert na 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];
}
Mo Farhand
źródło
Dzięki, ale po prostu szukam odpowiedzi, korzystając z alertu powiadomienia o stanie systemu iOS.
pkamb
1
ok, ale myślę, że nie możesz tego zrobić, spójrz też na ten link: stackoverflow.com/questions/14872088/…
Mo Farhand
6
@matt Postaram się utrzymać to pytanie w temacie: pokazanie na pierwszym planie alertu na iOS. Mnóstwo innych pytań dotyczących „najlepszej struktury alertów toastowych”. Odpowiedź „Nie, nie możesz pokazać alertu iOS na pierwszym planie” byłaby całkowicie akceptowalna i zaakceptuję tę odpowiedź, dopóki nie będzie to możliwe w przyszłej wersji iOS.
pkamb
4

Wersja Swift 3

To pokazuje alert, gdy aplikacja jest na pierwszym planie.

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

Implementacja 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
}
Leslie Godwin
źródło
tylko dla pewności ... czy masz na myśli to, że od czasu iOS 10 możesz teraz również wyświetlać alerty / banery / dźwięki na pierwszym planie, tak jak robisz to w tle?
Honey
Gdzie mogę umieścić powyższą część kodującą? w otrzymałaRemoteNotification?
user1960169
@Leslie Godwin będzie działać na iOS 8.0 do wyświetlania powiadomień na pierwszym planie
Dilip Tiwari
2

Aby wyświetlić powiadomienie lokalne, jest to najlepsza opcja. potrzeba mniej kodu, aby wczytać „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)
Mantosh Kumar
źródło
Ta BRYXBannerstruktura nie wydaje się użyć Stock powiadamiania baner iOS.
pkamb
1

Jeśli cel wdrożenia> = iOS10, użyj UNUserNotification, jak poniżej-

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