あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

Xamarin,iPhoneアプリでTableViewから値を渡して画面遷移する方法

はじめに

Xamarin.iOSiPhone アプリで、テーブルビューを表示して、行をタップすると詳細画面が表示されるという、いたってシンプルな画面遷移を実装しようと思ったのですが思いの外苦労しました。

Swift と Xamarin.iOSAPI は同じものを使っているのですが実装の方法が異なるようで、いざ実装しようと思うとかなり苦労することが多いですね。

今回は、テーブルビューで行をタップすると画面遷移し、値として URL を渡すことで遷移先の画面でサイトを表示する方法をご紹介します。

説明が難しいのですが、頑張って読み解いてみてください。

今回のゴール

RSSフィードもどきのデータを一覧画面で表示し、遷移先の画面で URL を WebView で表示させるのが今回のゴールになります。

具体的には以下のイメージ。

一覧画面で記事のタイトルを表示します。

f:id:fnyablog:20180908001627p:plain

行をタップすると、該当の URL が WebView で表示されます。

f:id:fnyablog:20180908001700p:plain

セキュリティ設定

今回 WebView で表示するのは当ブログの記事なのですが、まだ HTTPS に対応していません。ココログさん早く対応してくださいね。

なぜそんな話をしたかというと、iPhone アプリの WebView では HTTP 通信をブロックしてしまうためです。

これは、App Transport Security(ATS) というセキュリティ機能で、現在はデフォルトで有効になっています。

これを無効化する必要があるのですが、iPhone アプリを配布する段階では基本的に ATS の無効化は認められず、Apple に説明が必要とのことなのでご注意ください。

ATS を無効化するには、info.plist をテキストエディタで開いて以下の設定を追記します。

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

これで WebView で HTTP通信のコンテンツを表示することが可能になりました。

storyboard の設定

storyboard が以下のようになるように設定を行います。

f:id:fnyablog:20180908001859p:plain

Visual Studio for Mac での Xamarin.iOS の説明になりますが、これがなかなか簡単ではありませんでした。

Single View App のプロジェクトを新規に作成すると、デフォルトで storyboard に View Controller 、それと紐付いた ViewController.cs ファイルが作成されていますがどちらも削除します。

storyboard に TableViewControlelr をツールボックスからドラッグして配置します。

TableViewController を選択し、Class プロパティに「ListViewController」と入力後、Enterキーを押してください。すると、ListViewController.cs ファイルが作成されます。

f:id:fnyablog:20180908001947p:plain

TableViewを選択して、Nameプロパティに「tableView」を設定します。

f:id:fnyablog:20180908002037p:plain

TableViewCell を選択し、Styleプロパティに「Basic」を、Identifier プロパティに「Cell」を設定します。

f:id:fnyablog:20180908002114p:plain

ツールボックスから NavigationController を storyboard にドラッグして配置します。この際、なぜか TableViewControllerがついてきますが不要なので削除してください。

NavigationController の「Is Initial View Controller」にチェックを入れます。

f:id:fnyablog:20180908002150p:plain

コントロールキーを押したまま、NavigarionController から TableViewController にドラッグすると、メニューが表示されるのでそこで「Root」を選択します。

TableViewController にナビゲーションが表示されるので、Title プロパティに「一覧」と設定します。

f:id:fnyablog:20180908002220p:plain

ツールボックスから ViewController を storyboard にドラッグします。

ViewController の Classプロパティに「DetailViewController」と入力後、エンターキーを押すと、DetailViewController.cs ファイルが作成されます。

f:id:fnyablog:20180908002250p:plain

ツールボックスから WebKit View を DetailVIewController にドラッグして追加します。

WebKit View の Name プロパティに「webView」と設定します。

f:id:fnyablog:20180908002415p:plain

TableViewController から DetailViewController にコントロールを押しながらドラッグすると、メニューが表示されるので「Show」を選択します。

できあがったセグエの Identifier プロパティに「segue_id」を設定します。

f:id:fnyablog:20180908002446p:plain

これで一通りの storyboard の設定が済みました。

ソースコードの記述

ここまで来たらあとはソースコードを記述するだけです。

Item.cs ファイルを新規に追加して Item クラスを作成します。これは RSS フィードを模したデータを格納するクラスになります。

using System;
namespace PageNavigation
{
    public class Item
    {
        public string Title { get; set; }
        public string Link { get; set; }
    }
}

ListViewController.cs に以下のように記述します。

using Foundation;
using System;
using System.Collections.Generic;
using UIKit;

namespace PageNavigation
{
    public partial class ListViewController : UITableViewController
    {
        List<Item> items;

        public ListViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            //なんちゃってRSSデータを作成
            items = new List<Item>();

            items.Add(new Item
            {
                Title = "[Xamarin]Xamarin.iOSでiPhoneアプリにテーブルビューを作成する",
                Link = "http://fnya.cocolog-nifty.com/blog/2018/08/xamarinxamari-1.html"
            });
            items.Add(new Item
            {
                Title = "[Xamarin]iPhoneアプリのテーブルビューにカスタマイズしたセルを追加する",
                Link = "http://fnya.cocolog-nifty.com/blog/2018/08/xamariniphone-c.html"
            });
            items.Add(new Item
            {
                Title = "[Xamarin]iPhoneアプリでテーブルビューのセルにチェックボックスを表示する",
                Link = "http://fnya.cocolog-nifty.com/blog/2018/08/xamariniphone-7.html"
            });

            //テーブルビューのソースに作成したデータをセット
            this.tableView.Source = new TableViewSource(items);

        }

        //画面遷移する前に呼び出される、値受け渡しのためのメソッド
        public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
        {
            base.PrepareForSegue(segue, sender);


            if (segue.Identifier == "segue_id")
            {
                //クリックされたセルの行数を取得しitemを取得する
                var indexPath = this.tableView.IndexPathForSelectedRow;
                var row = indexPath.Row;
                var item = this.items[row];

                //セグエ経由で値の受け渡し先のビューコントロールを取得
                var controller = (DetailViewController)segue.DestinationViewController;

                //ビューコントローラに値を受け渡し
                controller.Link = item.Link;
            }
        }
    }


    //テーブルビューのソースを定義する追加クラス
    public class TableViewSource : UITableViewSource
    {
        private List<Item> items;

        public TableViewSource(List<Item> _items)
        {
            this.items = _items;
        }

        //データの行数を返す
        public override nint RowsInSection(UITableView tableview, nint section)
        {
            return this.items.Count;
        }


        //セルに値を設定して返す
        public override UITableViewCell GetCell(UITableView tableView, Foundation.NSIndexPath indexPath)
        {
            var cell = tableView.DequeueReusableCell("Cell");
            cell.TextLabel.Text = this.items[indexPath.Row].Title;
            return cell;
        }
    }
}

DetailViewController.cs ファイルに以下のように記述します。

using Foundation;
using System;
using UIKit;

namespace PageNavigation
{
    public partial class DetailViewController : UIViewController
    {
        public string Link { get; set; }

        public DetailViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            //リクエストを渡されたパラメータから作成
            var url = new NSUrl(this.Link);
            var request = new NSUrlRequest(url);

            //WebViewにURLの内容を読み込み
            this.webView.LoadRequest(request);
        }
    }
}

これでソースコードの記述も一通り終了です。

あとはデバッグ実行して、ゴールの画面が表示されれば完了です。

おわりに

Xamarin での iPhone アプリの実装は、Swift でできることと同じなのですが、記述方法が大きく異なるためある程度しか参考にならないと痛感しました。

Xamarin のサンプルはやはり Xamarin でないとなかなか厳しいですね。

Xamarin は書籍も少ないし、ネットの情報も少ないので苦労はするのだろうとは思います。

ただ、それでもやはり C# で開発ができ、.NET Framework の機能や NuGet パッケージを利用できるというのは大きなアドバンテージだと思います。