Botones de acción de notificación dinámica en Xamarin.iOS

En iOS 12, las notificaciones pueden agregar, quitar y actualizar dinámicamente sus botones de acción asociados. Esta personalización permite proporcionar a los usuarios acciones que sean directamente relevantes para el contenido de la notificación y la interacción del usuario con él.

Aplicación de ejemplo: RedGreenNotifications

Los fragmentos de código de esta guía proceden de la aplicación de ejemplo RedGreenNotifications, en la que se muestra cómo usar Xamarin.iOS para trabajar con botones de acción de notificación en iOS 12.

Esta aplicación de ejemplo envía dos tipos de notificaciones locales: rojas y verdes. Después de que la aplicación envíe una notificación, use 3D Touch para ver su interfaz de usuario personalizada. Después, use los botones de acción de la notificación para girar la imagen que muestra. A medida que la imagen gira, aparece y desaparece un botón Restablecer rotación, según sea necesario.

Los fragmentos de código de esta guía proceden de esta aplicación de ejemplo.

Botones de acción predeterminados

La categoría de una notificación determina sus botones de acción predeterminados.

Cree y registre categorías de notificación mientras se inicia una aplicación. Por ejemplo, en la aplicación de ejemplo, el método FinishedLaunching de AppDelegate hace lo siguiente:

  • Define una categoría para las notificaciones rojas y otra para las notificaciones verdes
  • Registra estas categorías mediante la llamada al método SetNotificationCategories de UNUserNotificationCenter
  • Adjunta una instancia de UNNotificationAction a cada categoría

En el siguiente código de muestra se muestra cómo funciona:

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
    // Request authorization to send notifications
    UNUserNotificationCenter center = UNUserNotificationCenter.Current;
    var options = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Provisional | UNAuthorizationOptions.ProvidesAppNotificationSettings;
    center.RequestAuthorization(options, (bool success, NSError error) =>
    {
        // ...
        var rotateTwentyDegreesAction = UNNotificationAction.FromIdentifier("rotate-twenty-degrees-action", "Rotate 20°", UNNotificationActionOptions.None);

        var redCategory = UNNotificationCategory.FromIdentifier(
            "red-category",
            new UNNotificationAction[] { rotateTwentyDegreesAction },
            new string[] { },
            UNNotificationCategoryOptions.CustomDismissAction
        );

        var greenCategory = UNNotificationCategory.FromIdentifier(
            "green-category",
            new UNNotificationAction[] { rotateTwentyDegreesAction },
            new string[] { },
            UNNotificationCategoryOptions.CustomDismissAction
        );

        var set = new NSSet<UNNotificationCategory>(redCategory, greenCategory);
        center.SetNotificationCategories(set);
    });
    // ...
}

En función de este código, cualquier notificación cuyo valor Content.CategoryIdentifier sea "red-category" o "green-category" mostrará, de manera predeterminada un botón de acción Girar 20°.

Control en la aplicación de botones de acción de notificación

UNUserNotificationCenter tiene una propiedad Delegate de tipo IUNUserNotificationCenterDelegate.

En la aplicación de ejemplo, AppDelegate se establece como delegado del centro de notificaciones de usuario en FinishedLaunching:

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
    // Request authorization to send notifications
    UNUserNotificationCenter center = UNUserNotificationCenter.Current;
    var options = // ...
    center.RequestAuthorization(options, (bool success, NSError error) =>
    {
        center.Delegate = this;
        // ...

Después, AppDelegate implementa DidReceiveNotificationResponse para controlar pulsaciones de botón de acción:

[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, System.Action completionHandler)
{
    if (response.IsDefaultAction)
    {
        Console.WriteLine("ACTION: Default");
    }
    if (response.IsDismissAction)
    {
        Console.WriteLine("ACTION: Dismiss");
    }
    else
    {
        Console.WriteLine($"ACTION: {response.ActionIdentifier}");
    }

    completionHandler();
        }

Esta implementación de DidReceiveNotificationResponse no controla el botón de acción Girar 20° de la notificación. En su lugar, la extensión de contenido de la notificación controla las pulsaciones en este botón. En la sección siguiente se describe aún más el control de botones de acción de notificación.

Botones de acción en la extensión de contenido de notificación

Una extensión de contenido de notificación contiene un controlador de vista que define la interfaz personalizada para una notificación.

Este controlador de vista puede usar los métodos GetNotificationActions y SetNotificationActions en su propiedad ExtensionContext para acceder a los botones de acción de la notificación y modificarlos.

En la aplicación de ejemplo, el controlador de vista de la extensión de contenido de notificación modifica los botones de acción solo cuando se responde a una pulsación en un botón de acción ya existente.

Nota:

Una extensión de contenido de notificación puede responder a una pulsación del botón de acción en el método DidReceiveNotificationResponse del controlador de vista, declarado como parte de IUNNotificationContentExtension.

Aunque comparte nombre con el método DidReceiveNotificationResponsedescrito anteriormente, este es un método diferente.

Después de que una extensión de contenido de notificación termine de procesar una pulsación de botón, puede elegir si quiere indicarle a la aplicación principal que controle esa misma pulsación de botón. Para ello, debe pasar un valor adecuado de UNNotificationContentExtensionResponseOption a su controlador de finalización:

  • Dismiss indica que se debe descartar la interfaz de notificación y que la aplicación principal no necesita controlar la pulsación del botón.
  • DismissAndForwardAction indica que se debe descartar la interfaz de notificación y que la aplicación principal tampoco necesita controlar la pulsación del botón.
  • DoNotDismiss indica que no se debe descartar la interfaz de notificación y que la aplicación principal no necesita controlar la pulsación del botón.

El método DidReceiveNotificationResponse de la extensión de contenido determina qué botón de acción se ha pulsado, gira la imagen en la interfaz de la notificación y muestra u oculta un botón de acción Restablecer:

[Export("didReceiveNotificationResponse:completionHandler:")]
public void DidReceiveNotificationResponse(UNNotificationResponse response, Action<UNNotificationContentExtensionResponseOption> completionHandler)
{
    var rotationAction = ExtensionContext.GetNotificationActions()[0];

    if (response.ActionIdentifier == "rotate-twenty-degrees-action")
    {
        rotationButtonTaps += 1;

        double radians = (20 * rotationButtonTaps) * (2 * Math.PI / 360.0);
        Xamagon.Transform = CGAffineTransform.MakeRotation((float)radians);

        // 9 rotations * 20 degrees = 180 degrees. No reason to
        // show the reset rotation button when the image is half
        // or fully rotated.
        if (rotationButtonTaps % 9 == 0)
        {
            ExtensionContext.SetNotificationActions(new UNNotificationAction[] { rotationAction });
        }
        else if (rotationButtonTaps % 9 == 1)
        {
            var resetRotationAction = UNNotificationAction.FromIdentifier("reset-rotation-action", "Reset rotation", UNNotificationActionOptions.None);
            ExtensionContext.SetNotificationActions(new UNNotificationAction[] { rotationAction, resetRotationAction });
        }
    }

    if (response.ActionIdentifier == "reset-rotation-action")
    {
        rotationButtonTaps = 0;

        double radians = (20 * rotationButtonTaps) * (2 * Math.PI / 360.0);
        Xamagon.Transform = CGAffineTransform.MakeRotation((float)radians);

        ExtensionContext.SetNotificationActions(new UNNotificationAction[] { rotationAction });
    }

    completionHandler(UNNotificationContentExtensionResponseOption.DoNotDismiss);
}

En este caso, el método pasa UNNotificationContentExtensionResponseOption.DoNotDismiss a su controlador de finalización. Esto significa que la interfaz de la notificación permanecerá abierta.