fc2ブログ

記事一覧

iOSでセカンダリツールバーをカスタマイズする | Xamarin.Forms


今回はiOSでセカンダリのツールバーをカスタマイズする方法についてご紹介いたします。通常の設定方法につきましては次の記事「ToolbarItemの表示方法」をご参考ください。
通常、ツールバーのセカンダリとして設定した場合、画面の上部(ナビゲーションバーのすぐ下)に大きく表示され、デザインも悪いので邪魔な表示ととらえられがちです。セカンダリツールバーを画面下部に表示するだけでだいぶすっきりとした印象に変わります。また、画面下部に表示できたとしてもアイコンが表示できていない例をよく見かけましたので、セカンダリツールバーのアイコンも正しく表示されるコードサンプルをご紹介いたします。


iOS After
xamarin_toolbar_secondary_01.png
iOS Before
xamarin_toolbar_secondary_02.png



前提条件
・Windows10 Pro 64Bit
・Visual Studio 2015 Community Update3
・Xamarin 4.3.0.784 (NuGet Xamarin.Forms 2.3.4.224)
・macOS Sierra 10.12.4 / Xcode8.3.1 / Xamarin.iOS 10.4.0.123



1.カスタムレンダラーを作成する

iOSプロジェクトに以下の2種類のカスタムレンダラーを作成します。
作成したファイルを配置して実行するだけでセカンダリのツールバーが画面下部に移動して表示されます。

PatchedNavigationRenderer.cs
using System;
using System.Collections.Generic;
using System.Text;
using UIKit;
using System.Linq;
using CoreGraphics;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;

[assembly: ExportRenderer(typeof(NavigationPage), typeof(AppName.iOS.Renderer.PatchedNavigationRenderer))]

namespace AppName.iOS.Renderer
{
    public class PatchedNavigationRenderer : NavigationRenderer
    {
        public override void ViewWillAppear(bool animated)
        {
            var badBar = View.Subviews.OfType<UIToolbar>().FirstOrDefault(v => v.GetType() != typeof(UIToolbar));
            if (badBar != null)
            {
                badBar.RemoveFromSuperview();
            }
            base.ViewWillAppear(animated);
        }

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

            UIView[] subviews = View.Subviews.Where(v => v != NavigationBar).ToArray();
            var toolBarViews = subviews.Where(v => v is UIToolbar).ToArray();

//Toolbar以外のViewの高さ調整は、Viewの下部がタッチできなくなるため必要ないと判断していますのでコメントアウト
            //var otherViews = subviews.Where(v => !(v is UIToolbar)).ToArray();

            nfloat toolbarHeight = 0;

            foreach (var uIView in toolBarViews)
            {
                uIView.SizeToFit();
                uIView.Frame = new CGRect
                {
                    X = 0,
                    Y = View.Bounds.Height - uIView.Frame.Height,
                    Width = View.Bounds.Width,
                    Height = uIView.Frame.Height,
                };
                var thisToolbarHeight = uIView.Frame.Height;
                if (toolbarHeight < thisToolbarHeight)
                {
                    toolbarHeight = thisToolbarHeight;
                }
            }

//Toolbar以外のViewの高さ調整は、Viewの下部がタッチできなくなるため必要ないと判断していますのでコメントアウト
            //var othersHeight = View.Bounds.Height - toolbarHeight;
            //var othersFrame = new CGRect(View.Bounds.X, View.Bounds.Y, View.Bounds.Width, othersHeight);
            //foreach (var uIView in otherViews)
            //{
            //    uIView.Frame = othersFrame;
            //}
        }
    }
}


ToolbarRenderer.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Windows.Input;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;
using UIKit;
using CoreGraphics;

[assembly: ExportRenderer(typeof(Page), typeof(AppName.iOS.Renderer.ToolbarRenderer))]

namespace AppName.iOS.Renderer
{
    public class ToolbarRenderer : PageRenderer
    {
        UIToolbar _toolbar;
        List<ToolbarItem> _secondaryItems;
        readonly Dictionary<UIBarButtonItem, ICommand> _buttonCommands = new Dictionary<UIBarButtonItem, ICommand>();

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            var page = e.NewElement as Page;
            if (page != null)
            {
                _secondaryItems = page.ToolbarItems.Where(i => i.Order == ToolbarItemOrder.Secondary).ToList();
                _secondaryItems.ForEach(t => page.ToolbarItems.Remove(t));
            }
            base.OnElementChanged(e);
        }

        public override void ViewWillAppear(bool animated)
        {
            if (_secondaryItems != null && _secondaryItems.Count > 0)
            {
                var tools = new List<UIBarButtonItem>();
                _buttonCommands.Clear();
                foreach (var tool in _secondaryItems)
                {
                    UIBarButtonItem button;
                    UIBarButtonSystemItem systemItem;
                    button = Enum.TryParse<UIBarButtonSystemItem>(tool.Text, out systemItem)
                        ? new UIBarButtonItem(systemItem, ToolClicked)
                        : new UIBarButtonItem(tool.Text, UIBarButtonItemStyle.Plain, ToolClicked);
                    button.Image = UIImage.FromFile(tool.Icon);     //Iconを設定
                    _buttonCommands.Add(button, tool.Command);
                    tools.Add(button);
                }

                NavigationController.SetToolbarHidden(false, animated);
                _toolbar = new UIToolbar(CGRect.Empty) { Items = tools.ToArray() };
                NavigationController.View.Add(_toolbar);
            }

            base.ViewWillAppear(animated);
        }

        void ToolClicked(object sender, EventArgs args)
        {
            var tool = sender as UIBarButtonItem;
            var command = _buttonCommands[tool];
            command.Execute(null);
        }

        public override void ViewWillDisappear(bool animated)
        {
            if (_toolbar != null)
            {
                NavigationController.SetToolbarHidden(true, animated);
                _toolbar.RemoveFromSuperview();
                _toolbar = null;
                _buttonCommands.Clear();
            }
            base.ViewWillDisappear(animated);
        }
    }
}

尚、ツールバーの設定はPCLプロジェクトで通常の設定を行うだけで表示が変更されます。



2.トラブルシューティング

もしも画面遷移時にセカンダリツールバーが表示されたままで消えないような場合は、以下のようにツールバーのインスタンスを親ビューから削除するコードを追加します。

protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
    _vece = e;
    var page = e.NewElement as Page;
    if (page != null)
    {
        //セカンダリ
        if (_secondaryItems != null && _secondaryItems.Count > 0)
        {
            if (_toolbar != null)
            {
                //必ず前に設定したツールバーを破棄する
                _toolbar.RemoveFromSuperview();
                _toolbar = null;
            }
            _secondaryItems.Clear();
            _secondaryItems = null;
        }
        _secondaryItems = page.ToolbarItems.Where(i => i.Order == ToolbarItemOrder.Secondary).ToList();
        _secondaryItems.ForEach(t => page.ToolbarItems.Remove(t));
    }
    base.OnElementChanged(e);
}




2017/11/14追記
iPhoneXの場合は全面ディスプレイになっている関係上、マイク部分がセカンダリツールバーに重なっていて見えにくくなっています。その対応方法につきましては次の記事「アプリをiPhoneXに対応させる方法」でご紹介しておりますので、ご参考ください。





当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
https://itblog.dynaspo.com/blog-entry-81.html


関連記事

コメント

コメントの投稿

※名前とタイトルが入力されていないコメントでは他のコメントとの区別ができません。

 入力されていないコメントには返信しませんのであらかじめご了承くださいませ。

※ニックネームでも良いので必ずご入力ください。

    

※必ずご入力ください。

    
    

※必ずご入力ください。

※技術的な質問には環境やエラーについて正確かつ詳細にお教えください。

・正確なエラーの内容

・Windowsのバージョン番号

・Visual Studioのバージョン

・機器の型番

・アプリやソフトのバージョン

    

カテゴリ別記事一覧

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Catalina 10.15 / Mojave 10.14 / High Sierra 10.13 / Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
【データベース】
Oracle 10g/9i
SQLServer 2016/2008R2/2005/2000
SQLAnywhere 16/11/8
【BI/レポートツール】
Cognos ReportNet (IBM)
Microsoft PowerBI
ActiveReport (GrapeCity)
CrystalReport
【OCX関連】
GrapeCity InputMan SPREAD MultiRow GridView
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS jQuery IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。