Xamarin.Forms Vista web

Download SampleDescargar el ejemplo

WebView es una vista para mostrar contenido web y HTML en la aplicación:

In App Browser

Contenido

WebView admite los siguientes tipos de contenido:

  • Sitios web HTML y CSS: WebView tiene compatibilidad completa con sitios web escritos mediante HTML y CSS, incluida la compatibilidad con JavaScript.
  • Documentos: dado que WebView se implementa mediante componentes nativos en cada plataforma, WebView es capaz de mostrar documentos en los formatos compatibles con la plataforma subyacente.
  • Cadenas HTML: WebView puede mostrar cadenas HTML de la memoria.
  • Archivos locales: WebView puede presentar cualquiera de los tipos de contenido anteriores insertados en la aplicación.

Nota:

WebView en Windows no admite Silverlight, Flash ni ningún control ActiveX, incluso si son compatibles con Internet Explorer en esa plataforma.

Sitios web

Para mostrar un sitio web de Internet, establezca la propiedad WebView de Source en una cadena URL:

var browser = new WebView
{
  Source = "https://dotnet.microsoft.com/apps/xamarin"
};

Nota:

Las direcciones URL deben estar totalmente formadas con el protocolo especificado (es decir, debe tener "http://" o "https://" antepuestos a él).

iOS y ATS

Desde la versión 9, iOS solo permitirá que la aplicación se comunique con los servidores que implementan la seguridad de procedimientos recomendados de forma predeterminada. Los valores deben establecerse en Info.plist para habilitar la comunicación con servidores no seguros.

Nota:

Si la aplicación requiere una conexión a un sitio web no seguro, siempre debe escribir el dominio como una excepción mediante NSExceptionDomains en lugar de desactivar ATS completamente mediante NSAllowsArbitraryLoads. NSAllowsArbitraryLoads solo debe utilizarse en situaciones de emergencia extremas.

A continuación se muestra cómo habilitar un dominio específico (en este caso xamarin.com) para omitir los requisitos de ATS:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>xamarin.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSTemporaryExceptionMinimumTLSVersion</key>
                <string>TLSv1.1</string>
            </dict>
        </dict>
    </dict>
    ...
</key>

Se recomienda habilitar solo algunos dominios para omitir ATS, lo que le permite usar sitios de confianza mientras se beneficia de la seguridad adicional en dominios que no son de confianza. A continuación se muestra el método menos seguro para deshabilitar ATS para la aplicación:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads </key>
        <true/>
    </dict>
    ...
</key>

Consulte Seguridad de transporte de aplicaciones para obtener más información sobre esta nueva característica en iOS 9.

Cadenas HTML

Si desea presentar una cadena de HTML definida dinámicamente en el código, deberá crear una instancia de HtmlWebViewSource:

var browser = new WebView();
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = @"<html><body>
  <h1>Xamarin.Forms</h1>
  <p>Welcome to WebView.</p>
  </body></html>";
browser.Source = htmlSource;

WebView Displaying HTML String

En el código anterior, @ se usa para marcar el HTML como literal de cadena textual, lo que significa que la mayoría de los caracteres de escape se omiten.

Nota:

Puede ser necesario establecer las propiedades WidthRequest y HeightRequest de WebView para ver el contenido HTML, dependiendo del diseño del elemento WebView secundario. Por ejemplo, esto es necesario en StackLayout.

Contenido HTML local

WebView puede mostrar contenido de HTML, CSS y JavaScript insertados en la aplicación. Por ejemplo:

<html>
  <head>
    <title>Xamarin Forms</title>
  </head>
  <body>
    <h1>Xamarin.Forms</h1>
    <p>This is an iOS web page.</p>
    <img src="XamarinLogo.png" />
  </body>
</html>

CSS:

html,body {
  margin:0;
  padding:10;
}
body,p,h1 {
  font-family: Chalkduster;
}

Tenga en cuenta que las fuentes especificadas en el CSS anterior deben personalizarse para cada plataforma, ya que no todas las plataformas tienen las mismas fuentes.

Para mostrar el contenido local mediante WebView, deberá abrir el archivo HTML como cualquier otro y, a continuación, cargar el contenido como una cadena en la propiedad Html de HtmlWebViewSource. Para obtener más información sobre cómo abrir archivos, vea Trabajar con archivos.

Las capturas de pantalla siguientes muestran el resultado de mostrar contenido local en cada plataforma:

WebView Displaying Local Content

Aunque se ha cargado la primera página, WebView no tiene conocimiento de dónde procede el código HTML. Se trata de un problema al tratar con páginas que hacen referencia a recursos locales. Algunos ejemplos de cuándo puede ocurrir incluyen cuando las páginas locales se vinculan entre sí, una página usa un archivo JavaScript independiente o un vínculo de página a una hoja de estilos CSS.

Para solucionar esto, debe decirle al WebView dónde encontrar los archivos en el sistema de archivos. Para ello, establezca la propiedad BaseUrl en HtmlWebViewSource utilizada por WebView.

Dado que el sistema de archivos de cada uno de los sistemas operativos es diferente, debe determinar esa dirección URL en cada plataforma. Xamarin.Forms expone DependencyService para resolver las dependencias en tiempo de ejecución en cada plataforma.

Para usar DependencyService, defina primero una interfaz que pueda implementarse en cada plataforma:

public interface IBaseUrl { string Get(); }

Tenga en cuenta que hasta que la interfaz se implemente en cada plataforma, la aplicación no se ejecutará. En el proyecto común, asegúrese de que recuerda establecer BaseUrl utilizando DependencyService:

var source = new HtmlWebViewSource();
source.BaseUrl = DependencyService.Get<IBaseUrl>().Get();

Las implementaciones de la interfaz para cada plataforma deben proporcionarse.

iOS

En iOS, el contenido web debe ubicarse en el directorio raíz del proyecto o en el directorio Resources con la acción de compilación BundleResource, como se demuestra a continuación:

El BaseUrl debe ajustarse a la ruta del paquete principal:

[assembly: Dependency (typeof (BaseUrl_iOS))]
namespace WorkingWithWebview.iOS
{
  public class BaseUrl_iOS : IBaseUrl
  {
    public string Get()
    {
      return NSBundle.MainBundle.BundlePath;
    }
  }
}

Android

En Android, coloque el HTML, el CSS y las imágenes en la carpeta Assets con la acción de compilación AndroidAsset como se demuestra a continuación:

En Android, el BaseUrl debe establecerse en "file:///android_asset/":

[assembly: Dependency (typeof(BaseUrl_Android))]
namespace WorkingWithWebview.Android
{
  public class BaseUrl_Android : IBaseUrl
  {
    public string Get()
    {
      return "file:///android_asset/";
    }
  }
}

En Android, también se puede acceder a los archivos de la carpeta Assets a través del contexto actual de Android, que se expone mediante la propiedad MainActivity.Instance:

var assetManager = MainActivity.Instance.Assets;
using (var streamReader = new StreamReader (assetManager.Open ("local.html")))
{
  var html = streamReader.ReadToEnd ();
}

Plataforma universal de Windows

En los proyectos de la plataforma universal de Windows (UWP), coloque HTML, CSS e imágenes en la raíz del proyecto con la acción de compilación establecida en Content.

BaseUrl debe establecerse en "ms-appx-web:///":

[assembly: Dependency(typeof(BaseUrl))]
namespace WorkingWithWebview.UWP
{
    public class BaseUrl : IBaseUrl
    {
        public string Get()
        {
            return "ms-appx-web:///";
        }
    }
}

WebView admite la navegación a través de varios métodos y propiedades que pone a disposición:

  • GoForward(): si CanGoForward es true, al llamar a GoForward se desplaza hacia adelante a la siguiente página visitada.
  • GoBack(): si CanGoBack es true, al llamar a GoBack se desplazará a la última página visitada.
  • CanGoBack: true si hay páginas a las que volver, false si el navegador se encuentra en la URL inicial.
  • CanGoForward: true si el usuario se ha desplazado hacia atrás y puede avanzar a una página ya visitada.

Dentro de las páginas, WebView no admite gestos multitáctil. Es importante asegurarse de que el contenido está optimizado para dispositivos móviles y que aparece sin necesidad de hacer zoom.

Es habitual que las aplicaciones muestren un enlace dentro de un WebView, en lugar del navegador del dispositivo. En esas situaciones, resulta útil permitir la navegación normal, pero cuando el usuario vuelve mientras se encuentra en el vínculo de inicio, la aplicación debe volver a la vista de la aplicación normal.

Use los métodos y propiedades de navegación integrados para habilitar este escenario.

Empiece por crear la página para la vista del explorador:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="WebViewSample.InAppBrowserXaml"
             Title="Browser">
    <StackLayout Margin="20">
        <StackLayout Orientation="Horizontal">
            <Button Text="Back" HorizontalOptions="StartAndExpand" Clicked="OnBackButtonClicked" />
            <Button Text="Forward" HorizontalOptions="EndAndExpand" Clicked="OnForwardButtonClicked" />
        </StackLayout>
        <!-- WebView needs to be given height and width request within layouts to render. -->
        <WebView x:Name="webView" WidthRequest="1000" HeightRequest="1000" />
    </StackLayout>
</ContentPage>

En el código subyacente:

public partial class InAppBrowserXaml : ContentPage
{
    public InAppBrowserXaml(string URL)
    {
        InitializeComponent();
        webView.Source = URL;
    }

    async void OnBackButtonClicked(object sender, EventArgs e)
    {
        if (webView.CanGoBack)
        {
            webView.GoBack();
        }
        else
        {
            await Navigation.PopAsync();
        }
    }

    void OnForwardButtonClicked(object sender, EventArgs e)
    {
        if (webView.CanGoForward)
        {
            webView.GoForward();
        }
    }
}

Eso es todo.

WebView Navigation Buttons

Eventos

WebView genera los siguientes eventos para ayudarle a responder a los cambios en el estado:

  • Navigating: evento generado cuando WebView comienza a cargar una nueva página.
  • Navigated: evento generado cuando se carga la página y la navegación se ha detenido.
  • ReloadRequested: evento generado cuando se realiza una solicitud para volver a cargar el contenido actual.

El objeto WebNavigatingEventArgs que acompaña al evento Navigating tiene cuatro propiedades:

  • Cancel: indica si se va a cancelar o no la navegación.
  • NavigationEvent: evento de navegación que se ha generado.
  • Source: obtiene el elemento que ha realizado la navegación.
  • Url: el destino de navegación.

El objeto WebNavigatedEventArgs que acompaña al evento Navigated tiene cuatro propiedades:

  • NavigationEvent: evento de navegación que se ha generado.
  • Result: describe el resultado de la navegación mediante un miembro de enumeración WebNavigationResult. Los valores válidos son Cancel, Failure, Success y Timeout.
  • Source: obtiene el elemento que ha realizado la navegación.
  • Url: el destino de navegación.

Si prevé usar páginas web que tardan mucho tiempo en cargarse, considere la posibilidad de usar los eventos Navigating y Navigated para implementar un indicador de estado. Por ejemplo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="WebViewSample.LoadingLabelXaml"
             Title="Loading Demo">
    <StackLayout>
        <!--Loading label should not render by default.-->
        <Label x:Name="labelLoading" Text="Loading..." IsVisible="false" />
        <WebView HeightRequest="1000" WidthRequest="1000" Source="https://dotnet.microsoft.com/apps/xamarin" Navigated="webviewNavigated" Navigating="webviewNavigating" />
    </StackLayout>
</ContentPage>

Los dos controladores de eventos:

void webviewNavigating(object sender, WebNavigatingEventArgs e)
{
    labelLoading.IsVisible = true;
}

void webviewNavigated(object sender, WebNavigatedEventArgs e)
{
    labelLoading.IsVisible = false;
}

Esto da como resultado la siguiente salida (carga):

Screenshot shows WebView Navigating Event while loading.

Carga finalizada:

Screenshot shows WebView Navigating Event after loading.

Recarga de contenido

WebView tiene un método Reload que se puede usar para volver a cargar el contenido actual:

var webView = new WebView();
...
webView.Reload();

Cuando se invoca el método Reload, se desencadena el evento ReloadRequested, lo que indica que se ha realizado una solicitud para recargar el contenido actual.

Rendimiento

Los exploradores web populares adoptan tecnologías como la representación acelerada por hardware y la compilación de JavaScript. Antes de Xamarin.Forms 4.4, Xamarin.FormsWebView se implementaba en iOS mediante la clase UIWebView. Sin embargo, muchas de estas tecnologías no estaban disponibles en esta implementación. Por lo tanto, desde Xamarin.Forms 4.4, Xamarin.FormsWebView se implementa en iOS mediante la clase WkWebView, que permite una navegación más rápida.

Nota:

En iOS, WkWebViewRenderer tiene una sobrecarga de constructor que acepta un argumento WkWebViewConfiguration. Esto permite configurar el representador al crearlo.

Una aplicación puede volver a utilizar la clase UIWebView de iOS para implementar Xamarin.FormsWebView, por razones de compatibilidad. Esto se puede lograr agregando el código siguiente al archivo AssemblyInfo.cs en el proyecto de la plataforma iOS para la aplicación:

// Opt-in to using UIWebView instead of WkWebView.
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(Xamarin.Forms.Platform.iOS.WebViewRenderer))]

Nota:

En Xamarin.Forms 5.0, se ha quitado la clase WebViewRenderer. Por lo tanto, Xamarin.Forms 5.0 no contiene una referencia al control UIWebView.

WebView en Android de forma predeterminada es tan rápido como el explorador integrado.

La vista web de UWP usa el motor de representación de Microsoft Edge. Los dispositivos de escritorio y tableta deben ver el mismo rendimiento que el uso del propio explorador Edge.

Permisos

Para que WebView funcione, debe asegurarse de que los permisos están establecidos para cada plataforma. Tenga en cuenta que en algunas plataformas funcionará WebView en modo de depuración, pero no cuando se compila para la versión. Esto se debe a que algunos permisos, como los de acceso a Internet en Android, se establecen de forma predeterminada por Visual Studio para Mac cuando están en modo de depuración.

  • UWP: requiere la funcionalidad internet (cliente y servidor) al mostrar contenido de red.
  • Android: solo requiere INTERNET al mostrar contenido de la red. El contenido local no requiere permisos especiales.
  • iOS: no requiere permisos especiales.

Layout

A diferencia de la mayoría de las otras vistas Xamarin.Forms, WebView requiere que se especifiquen HeightRequest y WidthRequest cuando está contenida en StackLayout o RelativeLayout. Si no especifica esas propiedades, WebView no se representará.

Los siguientes ejemplos demuestran disposiciones que dan como resultado WebViews que funcionan y se renderizan:

StackLayout con WidthRequest & HeightRequest:

<StackLayout>
    <Label Text="test" />
    <WebView Source="https://dotnet.microsoft.com/apps/xamarin"
        HeightRequest="1000"
        WidthRequest="1000" />
</StackLayout>

RelativeLayout con WidthRequest & HeightRequest:

<RelativeLayout>
    <Label Text="test"
        RelativeLayout.XConstraint= "{ConstraintExpression
                                      Type=Constant, Constant=10}"
        RelativeLayout.YConstraint= "{ConstraintExpression
                                      Type=Constant, Constant=20}" />
    <WebView Source="https://dotnet.microsoft.com/apps/xamarin"
        RelativeLayout.XConstraint="{ConstraintExpression Type=Constant,
                                     Constant=10}"
        RelativeLayout.YConstraint="{ConstraintExpression Type=Constant,
                                     Constant=50}"
        WidthRequest="1000" HeightRequest="1000" />
</RelativeLayout>

AbsoluteLayout sin WidthRequest & HeightRequest:

<AbsoluteLayout>
    <Label Text="test" AbsoluteLayout.LayoutBounds="0,0,100,100" />
    <WebView Source="https://dotnet.microsoft.com/apps/xamarin"
      AbsoluteLayout.LayoutBounds="0,150,500,500" />
</AbsoluteLayout>

Cuadrícula sin WidthRequest & HeightRequest. La cuadrícula es uno de los pocos diseños que no requiere especificar las alturas y anchuras solicitadas.:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="100" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Label Text="test" Grid.Row="0" />
    <WebView Source="https://dotnet.microsoft.com/apps/xamarin" Grid.Row="1" />
</Grid>

Invocación de JavaScript

WebView incluye la capacidad de invocar una función JavaScript desde C#, y devolver cualquier resultado al código C# que realiza la llamada. Esto se consigue con el método WebView.EvaluateJavaScriptAsync, que se muestra en el siguiente ejemplo de la muestra vista web:

var numberEntry = new Entry { Text = "5" };
var resultLabel = new Label();
var webView = new WebView();
...

int number = int.Parse(numberEntry.Text);
string result = await webView.EvaluateJavaScriptAsync($"factorial({number})");
resultLabel.Text = $"Factorial of {number} is {result}.";

El método WebView.EvaluateJavaScriptAsync evalúa el JavaScript especificado como argumento y devuelve cualquier resultado como string. En este ejemplo, se invoca la función factorial de JavaScript, que devuelve el factorial de number como resultado. Esta función de JavaScript se define en el archivo HTML local que carga WebView y se muestra en el ejemplo siguiente:

<html>
<body>
<script type="text/javascript">
function factorial(num) {
        if (num === 0 || num === 1)
            return 1;
        for (var i = num - 1; i >= 1; i--) {
            num *= i;
        }
        return num;
}
</script>
</body>
</html>

Cookies

Se pueden establecer cookies en un WebView, que luego se envían con la solicitud web a la URL especificada. Esto se consigue agregando objetos Cookie a un CookieContainer, que luego se establece como valor de la propiedad enlazable WebView.Cookies. El código siguiente muestra un ejemplo de esto:

using System.Net;
using Xamarin.Forms;
// ...

CookieContainer cookieContainer = new CookieContainer();
Uri uri = new Uri("https://dotnet.microsoft.com/apps/xamarin", UriKind.RelativeOrAbsolute);

Cookie cookie = new Cookie
{
    Name = "XamarinCookie",
    Expires = DateTime.Now.AddDays(1),
    Value = "My cookie",
    Domain = uri.Host,
    Path = "/"
};
cookieContainer.Add(uri, cookie);
webView.Cookies = cookieContainer;
webView.Source = new UrlWebViewSource { Url = uri.ToString() };

En este ejemplo, se agrega un único Cookie al objeto CookieContainer, que luego se establece como el valor de la propiedad WebView.Cookies. Cuando WebView envía una solicitud web a la dirección URL especificada, la cookie se envía con la solicitud.

Depreciación de UIWebView y rechazo de la App Store (ITMS-90809)

A partir de abril de 2020, Apple rechazará las apps que sigan utilizando la obsoleta API UIWebView. Aunque Xamarin.Forms ha cambiado a WKWebView como valor predeterminado, todavía existe una referencia al SDK más antiguo en los archivos binarios Xamarin.Forms. El comportamiento actual del enlazador iOS no elimina esto, y como resultado la obsoleta API UIWebView seguirá apareciendo como referenciada desde su aplicación cuando la envíe a la App Store.

Importante

En Xamarin.Forms 5.0, se ha quitado la clase WebViewRenderer. Por lo tanto, Xamarin.Forms 5.0 no contiene una referencia al control UIWebView.

Hay disponible una versión preliminar del enlazador para corregir este problema. Para activar la versión preliminar, deberá proporcionar un argumento adicional --optimize=experimental-xforms-product-type al enlazador.

Los requisitos para que esto funcione son:

  • Xamarin.Forms 4.5 o versiones posteriores. Xamarin.Forms 4.6, o versiones posteriores, es necesario si la aplicación usa Material Visual.
  • Xamarin.iOS 13.10.0.17 o versiones posteriores. Compruebe la versión de Xamarin.iOS en Visual Studio. Esta versión de Xamarin.iOS se incluye con Visual Studio para Mac 8.4.1 y Visual Studio 16.4.3.
  • Se eliminan referencias a UIWebView. El código no debe tener referencias a UIWebView ni a ninguna clase que use UIWebView.

Para obtener más información sobre cómo detectar y quitar referencias UIWebView, vea Desuso de UIWebView.

Configurar el enlazador

Siga estos pasos para que el enlazador quite las referencias UIWebView:

  1. Abra las propiedades del proyecto de iOS: haga clic con el botón derecho en el proyecto de iOS y elija Propiedades.
  2. Vaya a la sección Compilación de iOS: seleccione la sección Compilación de iOS.
  3. Actualización de los argumentos mtouch adicionales: en los Argumentos mtouch adicionales agregue esta marca --optimize=experimental-xforms-product-type (además de cualquier valor que ya pudiera estar ahí). Nota: esta marca funciona junto con el comportamiento del enlazador establecido en SDK Only o Link All. Si, por cualquier motivo, ve errores al establecer el comportamiento del enlazador en All, es más probable que se produzca un problema en el código de la aplicación o en una biblioteca de terceros que no sea segura del enlazador. Para obtener más información sobre el enlazador, consulte Vinculación de aplicaciones de Xamarin.iOS.
  4. Actualizar todas las configuraciones de compilación: use las listas configuración y plataforma en la parte superior de la ventana para actualizar todas las configuraciones de compilación. La configuración más importante que hay que actualizar es la de Release/iPhone, ya que es la que se suele utilizar para crear compilaciones para el envío a la App Store.

Puede ver la ventana con la nueva marca en su lugar en esta captura de pantalla:

Setting the flag in the iOS Build section

Ahora, al crear una nueva compilación (versión) y enviarla a App Store, no debería haber advertencias sobre la API en desuso.