ディーバ Blog

大阪発 C#の会社、株式会社ディーバの Blog です。

C# で国名の一覧を取得・表示する

CultureInfo, RegionInfo を使って国名(台湾・香港なども含む)の一覧を取得します。アプリで国籍の選択などに使えるかなと。

GetCultures でカルチャを取得し、LCID(ロケールID)プロパティを使って RegionInfo を生成します。Name プロパティが2文字のオブジェクトのみ使います。2文字の値は、ISO 3166 の国名コードです。2文字以外の値には “001” (世界)・"029" (カリブ)・"419" (ラテン アメリカ) があります。

var names = CultureInfo
    .GetCultures(CultureTypes.SpecificCultures)
    .Where(c => c.LCID != 4096 /* LOCALE_CUSTOM_UNSPECIFIED を除く */)
    .Select(c => new RegionInfo(c.LCID))
    .Where(r => r.Name.Length == 2)
    .Distinct()
    .OrderBy(r => r.DisplayName)
    .ToDictionary(r => r.Name, r => r.DisplayName);

Windows 10 で実行した結果です。139件ありました。

f:id:jz5_diva:20170120132840p:plain

すべての国が定義されているわけではないようです。CultureInfo.CreateSpecificCulture Method (String) (System.Globalization) の Remarks によると、Windows で定義されている一覧は [MS-LCID]: Appendix A: Product Behavior で参照できます。

参考として Wikipedia の 国の一覧 - Wikipedia では206ヶ国掲載されています。日本政府が承認した国家数は195ヶ国(日本除く)です。

他のサービスの国数も調べてみました。

  • Microsoft アカウントの国/地域は、254個
  • Google アカウントの国/地域は、255個
  • Tiwtter プロフィールの国は、155個と「世界中」

ディーバは、エクセルソフト「Xamarin 開発支援サービス」のパートナー企業です

株式会社ディーバは、エクセルソフト株式会社の提供する「Xamarin 開発支援サービス」の パートナー企業 に登録されました。

Xamarin 開発支援サービスについて

2017年1月11日 Xamarin 開発支援サービス プレスリリース より。

エクセルソフトは、Xamarin のエキスパートであるパートナー企業と提携し、モバイル アプリの開発支援サービスを提供開始

エクセルソフト株式会社(東京都港区)は、マイクロソフト社の Visual Studio に統合され、クロスプラットフォーム モバイル アプリの開発環境としてますます注目を集めている Xamarin のエキスパートでモバイル アプリの開発実績のあるパートナー企業と提携し、モバイル アプリの開発支援サービスを 2017 年 1 月 11 日より日本国内で提供開始しました。

株式会社ディーバについて

大阪発 C# の会社「株式会社ディーバ」は、平成4年創立以来 、最新の技術を自分たち自身で確かめ、その技術を元にお客様のさまざまなニーズに幅広くお答えしてきました。Xamarin を活用し最新技術を駆使した iOS / Android / Windows モバイルアプリケーションおよびデスクトップアプリケーション、さらにはサーバーアプリケーションも含めて幅広いソフトウエア開発ソリューションをご提供いたします。

詳細は以下のページをご参照ください。 株式会社ディーバ

商用でも無料の天気予報 API を使って Web ページに天気を表示

まったく天気を表示する必要はないのだけど、Web ページの少し空いたスペースに現在の天気でも表示したくなったので調べてみました。

f:id:jz5_diva:20161111185353p:plain

検索して見つけた simpleWeather.js は見た目が良いですが、Yahoo! の API を使っていたため、非営利の個人利用でしか使えません。

OpenWeatherMap

使ったみたのが OpenWeatherMap

  • Free プランは 60 リクエスト/分 などの制限
  • CC BY-SA 4.0

openweathermap.org

登録して API key を取得する必要があります。登録直後は API key を付けてもエラーレスポンスが返って来ましたが、少し待つと結果が得られました。

Dark Sky

今回は使いませんでしたが、もうひとつ Dark Sky というのもあります。

  • 1000リクエスト/日 まで無料
  • Powered by Dark Sky の表示とリンクが必要

darksky.net

昔は、Forecast.io というサイトだったようです。

Weather Icons

いい感じの天気アイコンは、Weather Icons フォントを使いました。

https://erikflowers.github.io/weather-icons/erikflowers.github.io

有名な天気 API は、レスポンス結果の天気を表すコードにもマッピングされていて、どのアイコンを使えばいいか迷う必要ありません。

実装

jQuery で API を呼び出し、レスポンスの JSON をもとに表示します。天気アイコンは昼と夜バージョンがあるのですが、sunrise と sunset の時刻を使い、日が沈んでいれば夜バージョンのアイコンを使いました。

<div class="jumbotron">
    <div id="today" class="text-primary text-center">@DateTime.UtcNow.AddHours(9).ToString("dddd, MMMM dd", new System.Globalization.CultureInfo("en-US"))</div>
    <div id="weather" class="text-center" style="display:none;">
        <div class="weather-info">
            <i class="wi" id="icon"></i>
            <span id="temp"></span><i class="wi wi-celsius"></i>
        </div>
        <ul class="list-inline">
            <li id="name"></li>
            <li id="desc"></li>
        </ul>
    </div>
</div>
$(function () {
    $.getJSON("http://api.openweathermap.org/data/2.5/weather?q=Osaka-shi,JP&APPID=xxx", function (data) {
        if (!data.cod || data.cod != 200) {
            return;
        }

        $("#desc").text(data.weather[0].description);
        $("#temp").text(Math.floor(data.main.temp - 273.15));
        $("#name").text(data.name);

        var id = data.weather[0].id;
        var unixTime = Math.floor((new Date()).getTime() / 1000);
        if (data.sys.sunrise <= unixTime && unixTime <= data.sys.sunset) {
            $("#icon").addClass("wi-owm-day-" + id);
        } else {
            $("#icon").addClass("wi-owm-night-" + id);
        }

        $("#weather").show();
    });
});

いい感じの JavaScript の実装を探していたのですが見つからず。JSON の中身を見て、自分で書いた方が早かったです。結果は、冒頭の画像です。

12/6 室内型プロジェクションマッピングやVR技術が魅せる新しいテーマパークの可能性

2016年12月6日〜8日にビッグサイトで開催される、テーマパーク・遊園地・公園の展示会「テーマパークEXPO 2016(PARX)」でのセミナーに、株式会社ディーバ 代表取締役 青柳臣一が登壇します。

室内型プロジェクションマッピングやVR技術が魅せる新しいテーマパークの可能性

最近非常に話題になっているAR、VR、MR技術とはどういうものなのか、なにができるのか、そして、どうやって作るのかを技術的な側面も踏まえた上で基本的なところをご紹介します。また、弊社が持つ室内型プロジェクションマッピング技術やこれらを組み合わせたときの可能性についてもご紹介します。

  • 2016/12/6 12:30-13:30
  • C-2 会場

セミナー/イベントのご案内|テーマパークEXPO 2016(PARX) より「VR」をキーワードに入れると検索できます。

f:id:diva_osaka:20161109121634p:plain

Xamarin.iOS 透明な NavigationBar と Toolbar

Xamarin に限らず iOS の一般的な話と同じですが、Xamarin.iOS で NavigationBar と Toolbar の背景を透明にします。

背景を透明にするだけでは、NavigationBar の下、Toolbar の上部分に線が表示されるので、それも非表示にします。

f:id:jz5_diva:20161026115947p:plain

public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);

    this.NavigationController.NavigationBar.TintColor = UIColor.White;
    this.NavigationController.NavigationBar.TitleTextAttributes = new UIStringAttributes()
    {
        ForegroundColor = UIColor.White
    };
    this.NavigationController.NavigationBar.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); // 背景透明
    this.NavigationController.NavigationBar.ShadowImage = new UIImage(); // 境界線透明

    this.NavigationController.Toolbar.TintColor = UIColor.White;
    this.NavigationController.Toolbar.SetBackgroundImage(new UIImage(), UIToolbarPosition.Any, UIBarMetrics.Default); // 背景透明
    this.NavigationController.Toolbar.ClipsToBounds = true; // 境界線非表示
}

Xamarin.iOS でフォルダー選択の UI (TreeView) を作る

iOS には、階層構造を表現・選択する UI コントロールが提供されていません。

検索すると、Xamarin iOS – Create custom TreeView control for iPad / iPhone « Milen's Blog で実装しているコードがありましたが、きちんと動作していない感じだったので、作って見ました。コードは、diva-osaka/xamarin-ios-treeviewsample にあります。

f:id:jz5_diva:20161020130040p:plain

フォルダーを閉じると、これまで開いていた情報は忘れます(再度 開くと直下の子要素しか表示しません)。

ノートを表すクラス。とりあえず表示と動作に必要なプロパティしかありません。

using System.Collections.Generic;

namespace TreeViewSample
{
    public class TreeNode
    {
        public List<TreeNode> Children { get; set; } = new List<TreeNode>();

        public string Name { get; set; }

        public int Level { get; set; }

        public bool IsExpanded { get; set; }
    }
}

UITableViewController を元に作成。

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

namespace TreeViewSample
{
    public partial class FolderViewController : UITableViewController
    {
        public FolderViewController(IntPtr handle) : base(handle)
        {
        }

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

            TableView.RowHeight = FolderCell.Height;
            TableView.SeparatorColor = UIColor.Clear;
            TableView.Source = new TreeViewSource(CreateNodes());
        }

        private TreeNode CreateNodes()
        {
            var osaka = new TreeNode { Level = 0, Name = "大阪市" };

            var sub1 = new TreeNode { Level = 1, Name = "中央区" };
            var sub2 = new TreeNode { Level = 1, Name = "北区" };
            var sub3 = new TreeNode { Level = 1, Name = "西区" };

            osaka.Children.Add(sub1);
            osaka.Children.Add(sub2);
            osaka.Children.Add(sub3);

            var sub4 = new TreeNode { Level = 2, Name = "淡路町" };
            sub1.Children.Add(sub4);

            return osaka;
        }

        private class TreeViewSource : UITableViewSource
        {
            private List<TreeNode> Nodes = new List<TreeNode>();

            public TreeViewSource(TreeNode root)
            {
                Nodes.Add(root);
            }

            public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
            {
                var cell = tableView.DequeueReusableCell("FolderCell") as FolderCell;
                cell.SetCellContents(Nodes[indexPath.Row]);
                return cell;
            }

            public override nint RowsInSection(UITableView tableview, nint section)
            {
                return Nodes.Count;
            }

            public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
            {
                var selectedNode = Nodes[indexPath.Row];
                var selectedIndex = indexPath.Row;

                if (!selectedNode.IsExpanded)
                {
                    var children = selectedNode.Children;

                    // 開く
                    if (!children.Any())
                    {
                        return;
                    }

                    selectedNode.IsExpanded = true;

                    var indexPaths = new List<NSIndexPath>();
                    foreach (var node in children.Select((Value, Index) => new { Value, Index }))
                    {
                        node.Value.IsExpanded = false;
                        indexPaths.Add(NSIndexPath.FromRowSection(selectedIndex + node.Index + 1, 0));
                    }
                    Nodes.InsertRange(selectedIndex + 1, children);
                    tableView.InsertRows(indexPaths.ToArray(), UITableViewRowAnimation.Automatic);
                }
                else
                {
                    // 閉じる
                    selectedNode.IsExpanded = false;

                    var node = Nodes.Skip(selectedIndex + 1).FirstOrDefault(i => i.Level <= selectedNode.Level);
                    var deleteCount = (node != null) ?
                        Nodes.IndexOf(node) - selectedIndex - 1 :
                        Nodes.Count - selectedIndex - 1;

                    var indexPaths = new List<NSIndexPath>();
                    for (int i = 0; i < deleteCount; i++)
                    {
                        Nodes.RemoveAt(selectedIndex + 1);
                        tableView.DeleteRows(new NSIndexPath[] { NSIndexPath.FromRowSection(selectedIndex + 1, 0) }, UITableViewRowAnimation.Top);
                    }
                }
            }
        }
    }
}
using CoreGraphics;
using Foundation;
using System;
using UIKit;

namespace TreeViewSample
{
    [Register("FolderCell")]
    public class FolderCell : UITableViewCell
    {
        static readonly public int Height = 52;

        private int level;
        private UIImageView imageView;
        private UILabel titleLabel;

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

        void ReleaseDesignerOutlets()
        {
        }

        public void SetCellContents(TreeNode node)
        {
            level = node.Level;

            imageView = new UIImageView
            {
                Image = UIImage.FromFile("Folder.png"),
                ContentMode = UIViewContentMode.Left
            };
            imageView.SizeToFit();

            titleLabel = new UILabel()
            {
                TextColor = UIColor.Black,
                BackgroundColor = UIColor.Clear,
                Text = node.Name
            };
            titleLabel.SizeToFit();

            foreach (var v in ContentView.Subviews)
            {
                v.RemoveFromSuperview();
            }

            ContentView.AddSubviews(imageView, titleLabel);
        }

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

            var indent = 28;
            var margin = 8;

            var imageFrame = new CGRect(level * indent + margin, 0, 28.0f, Height);
            imageView.Frame = imageFrame;

            var titleFrame = new CGRect(level * indent + imageFrame.Width + margin * 2, 0, (float)titleLabel.Bounds.Width, Height);
            titleLabel.Frame = titleFrame;
        }
    }
}