Xamarin.Forms WebView
WebView
は、アプリで Web および HTML コンテンツを表示するためのビューです。
コンテンツ
WebView
では、次の種類のコンテンツがサポートされています。
- HTML および CSS Web サイト – WebView では、JavaScript のサポートを含め、HTML と CSS を使用して記述された Web サイトが完全にサポートされています。
- ドキュメント – WebView は各プラットフォームのネイティブ コンポーネントを使用して実装されるため、WebView は基になるプラットフォームでサポートされている形式でドキュメントを表示できます。
- HTML 文字列 – WebView ではメモリからの HTML 文字列を表示できます。
- ローカル ファイル – WebView では、アプリに埋め込まれた上記の種類のコンテンツのいずれかを提示できます。
Note
Windows 上の WebView
では、そのプラットフォーム上の Internet Explorer でサポートされている場合でも、Silverlight、Flash、ActiveX コントロールはサポートされません。
Web サイト
インターネットから Web サイトを表示するには、WebView
の Source
プロパティを文字列 URL に設定します。
var browser = new WebView
{
Source = "https://dotnet.microsoft.com/apps/xamarin"
};
Note
URL は、指定されたプロトコルで完全に形成されている必要があります (つまり、"http://" または "https://" を先頭に付ける必要があります)。
iOS と ATS
バージョン 9 以降、iOS では、既定でベスト プラクティス セキュリティを実装するサーバーとアプリケーションとの通信のみが許可されます。 セキュリティで保護されていないサーバーとの通信を有効にするには、Info.plist
で値を設定する必要があります。
Note
アプリケーションでセキュリティ保護されていない Web サイトへの接続が必要な場合は、NSAllowsArbitraryLoads
を使用して ATS を完全にオフにするのではなく、常に NSExceptionDomains
を使って例外としてドメインを入力する必要があります。 NSAllowsArbitraryLoads
は極端な緊急時にのみ使用する必要があります。
特定のドメイン (この場合は xamarin.com) で 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>
一部のドメインのみが ATS をバイパスできるようにすることをお勧めします。これにより、信頼されていないドメインで追加のセキュリティを利用しながら、信頼済みサイトを使用できます。 次に、アプリの ATS を無効にする安全性の低い方法を示します。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads </key>
<true/>
</dict>
...
</key>
iOS 9 のこの新機能の詳細については、「アプリケーション トランスポート セキュリティ」を参照してください。
HTML 文字列
コードで動的に定義された HTML の文字列を提示する場合は、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;
上記のコードでは、@
を使用して HTML を逐語的文字列リテラルとしてマークします。つまり、ほとんどのエスケープ文字は無視されます。
Note
WebView
が子であるレイアウトによっては、WebView
の WidthRequest
および HeightRequest
プロパティを設定して HTML コンテンツを表示することが必要な場合があります。 たとえば、これは StackLayout
で必要です。
ローカル HTML コンテンツ
WebView では、アプリ内に埋め込まれた HTML、CSS、JavaScript のコンテンツを表示できます。 次に例を示します。
<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;
}
上記の CSS で指定されたフォントは、プラットフォームごとにカスタマイズする必要があることに注意してください。すべてのプラットフォームに同じフォントがあるわけではありません。
WebView
を使用してローカル コンテンツを表示するには、他と同様に HTML ファイルを開き、そのコンテンツを文字列として HtmlWebViewSource
の Html
プロパティに読み込む必要があります。 ファイルを開く方法の詳細については、ファイルの処理に関するページを参照してください。
次のスクリーンショットは、各プラットフォームでローカル コンテンツを表示した結果を示しています。
最初のページは読み込まれていますが、WebView
では HTML の取得元が認識されません。 これは、ローカル リソースを参照するページを処理するときの問題です。 このような場合の例としては、ローカル ページが相互にリンクされている場合、ページで別の JavaScript ファイルが利用されている場合、ページが CSS スタイルシートにリンクされている場合などがあります。
これを解決するには、ファイルシステム上のファイルを見つける場所を WebView
に指示する必要があります。 これを行うには、WebView
で使用される HtmlWebViewSource
に BaseUrl
プロパティを設定します。
各オペレーティング システム上のファイルシステムは異なるため、各プラットフォームでその URL を決定する必要があります。 Xamarin.Forms では、各プラットフォームで実行時に依存関係を解決するための DependencyService
が公開されます。
DependencyService
を使用するには、まず、各プラットフォームに実装できるインターフェイスを定義します。
public interface IBaseUrl { string Get(); }
各プラットフォームでインターフェイスが実装されるまで、アプリは実行されないことに注意してください。 一般的なプロジェクトでは、DependencyService
を使用して BaseUrl
を必ず設定してください。
var source = new HtmlWebViewSource();
source.BaseUrl = DependencyService.Get<IBaseUrl>().Get();
その後、各プラットフォームのインターフェイスの実装を提供する必要があります。
iOS
iOS では、次に示すように、Web コンテンツはプロジェクトのルート ディレクトリまたは Resources ディレクトリにビルド アクション BundleResource を使用して配置する必要があります。
BaseUrl
は、メイン バンドルのパスに設定する必要があります。
[assembly: Dependency (typeof (BaseUrl_iOS))]
namespace WorkingWithWebview.iOS
{
public class BaseUrl_iOS : IBaseUrl
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Android
Android では、次に示すように、ビルド アクション AndroidAsset を使用して、HTML、CSS、画像を Assets フォルダーに配置します。
Android では、BaseUrl
を "file:///android_asset/"
に設定する必要があります。
[assembly: Dependency (typeof(BaseUrl_Android))]
namespace WorkingWithWebview.Android
{
public class BaseUrl_Android : IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
Android では、Assets フォルダー内のファイルには、MainActivity.Instance
プロパティによって公開される現在の Android コンテキストを介してアクセスすることもできます。
var assetManager = MainActivity.Instance.Assets;
using (var streamReader = new StreamReader (assetManager.Open ("local.html")))
{
var html = streamReader.ReadToEnd ();
}
ユニバーサル Windows プラットフォーム
ユニバーサル Windows プラットフォーム (UWP) プロジェクトでは、Content に設定されたビルド アクションを使用してプロジェクト ルートに HTML、CSS、画像を配置します。
BaseUrl
は "ms-appx-web:///"
に設定する必要があります。
[assembly: Dependency(typeof(BaseUrl))]
namespace WorkingWithWebview.UWP
{
public class BaseUrl : IBaseUrl
{
public string Get()
{
return "ms-appx-web:///";
}
}
}
ナビゲーション
WebView では、使用できるいくつかのメソッドとプロパティのナビゲーションがサポートされています。
- GoForward() –
CanGoForward
が true の場合、GoForward
を呼び出すと、次にアクセスしたページに進みます。 - GoBack() –
CanGoBack
が true の場合、GoBack
を呼び出すと、最後にアクセスしたページに移動します。 - CanGoBack – 戻るページがある場合は
true
、ブラウザーが開始 URL にある場合はfalse
。 - CanGoForward – ユーザーが後方に移動し、既にアクセスされたページに進むことができる場合は
true
。
ページ内では、WebView
でマルチタッチ ジェスチャはサポートされません。 コンテンツがモバイルに最適化され、ズームを必要とせずに表示されるようにすることが重要です。
アプリケーションでは、デバイスのブラウザーではなく、WebView
内にリンクが表示されるのが一般的です。 このような状況では、通常のナビゲーションを許可すると便利ですが、ユーザーが開始リンク上にいる間にヒット バックすると、アプリは通常のアプリ ビューに戻る必要があります。
このシナリオを有効にするには、組み込みのナビゲーション メソッドとプロパティを使用します。
まず、ブラウザー ビューのページを作成します。
<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>
コードビハインドで次のようにします。
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();
}
}
}
これで完了です。
Events
WebView では、状態の変化に対応するのに役立つように、次のイベントが発生します。
Navigating
– WebView で新しいページの読み込みが開始されたときに発生するイベント。Navigated
– ページが読み込まれ、ナビゲーションが停止したときに発生するイベント。ReloadRequested
– 現在のコンテンツを再読み込みする要求が行われたときに発生するイベント。
Navigating
イベントに付随する WebNavigatingEventArgs
オブジェクトには、次の 4 つのプロパティがあります。
Cancel
– ナビゲーションを取り消すかどうかを示します。NavigationEvent
- 発生したナビゲーション イベント。Source
- ナビゲーションを実行した要素。Url
– ナビゲーション先。
Navigated
イベントに付随する WebNavigatedEventArgs
オブジェクトには、次の 4 つのプロパティがあります。
NavigationEvent
- 発生したナビゲーション イベント。Result
–WebNavigationResult
列挙メンバーを使用して、ナビゲーションの結果について説明します。 有効な値は、Cancel
、Failure
、Success
、およびTimeout
です。Source
- ナビゲーションを実行した要素。Url
– ナビゲーション先。
読み込みに時間がかかる Web ページの使用が予想される場合は、Navigating
および Navigated
イベントを使って状態インジケーターを実装することを検討してください。 次に例を示します。
<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>
2 つのイベント ハンドラー:
void webviewNavigating(object sender, WebNavigatingEventArgs e)
{
labelLoading.IsVisible = true;
}
void webviewNavigated(object sender, WebNavigatedEventArgs e)
{
labelLoading.IsVisible = false;
}
この結果、次の出力が得られます (読み込み)。
読み込みが完了:
コンテンツの再読み込み
WebView
には、現在のコンテンツを再度読み込むために使用できる Reload
メソッドがあります。
var webView = new WebView();
...
webView.Reload();
Reload
メソッドが呼び出されると、現在のコンテンツを再読み込みする要求が行われていることを示す ReloadRequested
イベントが発生します。
パフォーマンス
一般的な Web ブラウザーでは、ハードウェア アクセラレータ レンダリングや JavaScript コンパイルなどのテクノロジが採用されています。 Xamarin.Forms 4.4 より前では、Xamarin.FormsWebView
は UIWebView
クラスによって iOS に実装されていました。 しかし、これらのテクノロジの多くはこの実装では使用できませんでした。 したがって、Xamarin.Forms 4.4 以降、Xamarin.FormsWebView
は、より高速なブラウズをサポートする WkWebView
クラスによって iOS に実装されます。
Note
iOS では、WkWebViewRenderer
には、WkWebViewConfiguration
引数を受け入れるコンストラクター オーバーロードがあります。 これにより、作成時にレンダラーを構成できます。
アプリケーションは、互換性上の理由から、Xamarin.FormsWebView
を実装するために iOS UIWebView
クラスを使用するように戻すことができます。 これは、アプリケーションの iOS プラットフォーム プロジェクトの AssemblyInfo.cs ファイルに次のコードを追加して実現できます。
// Opt-in to using UIWebView instead of WkWebView.
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(Xamarin.Forms.Platform.iOS.WebViewRenderer))]
Note
Xamarin.Forms 5.0 では、WebViewRenderer
クラスは削除されました。 したがって、Xamarin.Forms 5.0 には、UIWebView
コントロールへの参照は含まれません。
Android のWebView
は、既定では、組み込みのブラウザーと同じくらい高速です。
UWP WebView では、Microsoft Edge レンダリング エンジンが使用されます。 デスクトップおよびタブレット デバイスでは、Edge ブラウザー自体を使用する場合と同じパフォーマンスが見られるはずです。
アクセス許可
WebView
を動作させるには、プラットフォームごとにアクセス許可が設定されていることを確認する必要があります。 一部のプラットフォームでは、WebView
はデバッグ モードで動作しますが、リリース用にビルドされた場合は動作しないことに注意してください。 これは、Android 上のインターネット アクセス用のアクセス許可など、一部のアクセス許可は、デバッグ モードの場合に Visual Studio for Mac によって既定で設定されるためです。
- UWP – ネットワーク コンテンツを表示するときに、インターネット (クライアントとサーバー) の機能が必要です。
- Android – ネットワークからコンテンツを表示する場合にのみ、
INTERNET
が必要です。 ローカル コンテンツには特別なアクセス許可は必要ありません。 - iOS – 特別なアクセス許可は必要ありません。
Layout
他のほとんどの Xamarin.Forms ビューとは異なり、WebView
では、StackLayout または RelativeLayout に含まれるときに HeightRequest
と WidthRequest
を指定する必要があります。 これらのプロパティを指定しないと、WebView
はレンダリングされません。
次の例は、WebView
が動作し、レンダリングされるレイアウトを示しています。
WidthRequest と HeightRequest を使用した StackLayout:
<StackLayout>
<Label Text="test" />
<WebView Source="https://dotnet.microsoft.com/apps/xamarin"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>
WidthRequest と HeightRequest を使用した RelativeLayout:
<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>
WidthRequest と HeightRequest を "使用しない" AbsoluteLayout:
<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>
WidthRequest と HeightRequest を "使用しない" Grid。 Grid は、要求された高さと幅を指定する必要のない数少ないレイアウトの 1 つです。
<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>
JavaScript の呼び出し
WebView
には、C# から JavaScript 関数を呼び出し、呼び出し元の C# コードに結果を返す機能が含まれています。 それには WebView.EvaluateJavaScriptAsync
メソッドを使います。
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}.";
WebView.EvaluateJavaScriptAsync
メソッドは、引数として指定された JavaScript を評価し、結果を string
として返します。 この例では、factorial
JavaScript 関数が呼び出され、結果として number
の階乗が返されます。 この JavaScript 関数は、WebView
が読み込むローカル HTML ファイルで定義されており、次の例に示されています。
<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>
Cookie
Cookie は WebView
で設定でき、Web 要求と共に指定された URL に送信されます。 これを行うには、Cookie
オブジェクトを CookieContainer
に追加し、WebView.Cookies
バインド可能なプロパティの値として設定します。 この例を次のコードに示します。
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() };
この例では、1 つの Cookie
を CookieContainer
オブジェクトに追加し、WebView.Cookies
プロパティの値として設定します。 WebView
が指定したURL に Web 要求を送信すると、要求と共に Cookie が送信されます。
UIWebView の非推奨と App Store の拒否 (ITMS-90809)
2020 年 4 月以降、非推奨の UIWebView
API を引き続き使用するアプリは Apple によって拒否されます。 Xamarin.Forms が既定として WKWebView
に切り替わりましたが、Xamarin.Forms バイナリには古い SDK への参照がまだあります。 現在の iOS リンカーの動作ではこれは削除されないため、App Store に送信するときに非推奨の UIWebView
API は引き続きアプリから参照されているように見えます。
重要
Xamarin.Forms 5.0 では、WebViewRenderer
クラスは削除されました。 したがって、Xamarin.Forms 5.0 には、UIWebView
コントロールへの参照は含まれません。
この問題を解決するためにリンカーのプレビュー バージョンを使用できます。 プレビューを有効にするには、リンカーに追加の引数 --optimize=experimental-xforms-product-type
を指定する必要があります。
これを機能させるための前提条件は次のとおりです。
- Xamarin.Forms 4.5 以上。 アプリで素材のビジュアルを使用する場合は、Xamarin.Forms 4.6 以降が必要です。
- Xamarin.iOS 13.10.0.17 以降。 Visual Studio で Xamarin.iOS のバージョンを確認します。 このバージョンの Xamarin.iOS は、Visual Studio for Mac 8.4.1 および Visual Studio 16.4.3 に含まれています。
UIWebView
への参照を削除する。 コードには、UIWebView
への参照や、UIWebView
を利用するクラスを含めないようにする必要があります。
UIWebView
参照の検出と削除の詳細については、「UIWebView の非推奨」を参照してください。
リンカーを構成する
リンカーで UIWebView
参照を削除するには、次の手順に従います。
- iOS プロジェクトのプロパティを開く – iOS プロジェクトを右クリックし、[プロパティ] を選択します。
- [iOS ビルド] セクションに移動する – [iOS ビルド] セクションを選択します。
- [追加の mtouch 引数] を更新する – [追加の mtouch 引数] では、このフラグ
--optimize=experimental-xforms-product-type
を追加します (既にある可能性がある値に加えて)。 注: このフラグは、[SDK のみ] または [すべてリンク] に設定されている [リンカーの動作] と連携します。 何らかの理由で、[リンカーの動作] を [すべて] に設定するときにエラーが表示される場合、これは、ほとんどの場合、アプリ コード、またはリンカー セーフではないサード パーティ製ライブラリ内の問題です。 リンカーの詳細については、「Xamarin.iOS アプリをリンクする」を参照してください。 - すべてのビルド構成を更新する – ウィンドウの上部にある [構成] および [プラットフォーム] リストを使用して、すべてのビルド構成を更新します。 更新する最も重要な構成は [リリース/iPhone] 構成です。これは通常、App Store 送信用のビルドを作成するために使用されるためです。
このスクリーンショットでは、新しいフラグが設定されたウィンドウを確認できます。
これで、新しい (リリース) ビルドを作成して App Store に送信するときに、非推奨の API に関する警告は表示されないはずです。