iOS には、階層構造を表現・選択する UI コントロールが提供されていません。
検索すると、Xamarin iOS – Create custom TreeView control for iPad / iPhone « Milen's Blog で実装しているコードがありましたが、きちんと動作していない感じだったので、作って見ました。コードは、diva-osaka/xamarin-ios-treeviewsample にあります。
フォルダーを閉じると、これまで開いていた情報は忘れます(再度 開くと直下の子要素しか表示しません)。
ノートを表すクラス。とりあえず表示と動作に必要なプロパティしかありません。
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;
}
}
}