こんにちは!今回ブログを担当することになりましたエンジニアの山元です。
デザイナー業務では、画像のリサイズ処理を必要とする状況がしばしば見受けられます。
そのため迅速に対応できる支援ツールがあれば負担軽減に繋がると考え、現在Xamarin.Macを利用して実装しています。
画像入力が簡単になるよう、ドラッグ&ドロップ機能を入れたいと調査していたのですが、
あまり情報を見つけられなかったこともあり、一連の実装作業をメモとして残そうと思います。
同じようなことをしたいと考えている方がいれば、参考にして頂ければ幸いです。
準備
実装環境は以下の通りです。
・macOS Catalina 10.15.4
・Visual Studio for Mac 8.5.4
・Xcode 11.5
初めに、Visual Studioで新しいプロジェクトを作成しておきます。
Cocoaアプリを選択し、任意のプロジェクト名と作成場所を設定する手順を踏みます。
↓ 作成されたプロジェクト
実装手順
以下の手順で進めます。
- Storyboard上のViewControllerScene内にCustomViewを配置し、操作用クラス”DropImageView”を設定
- 設定した”DropImageView”クラスに、ドラッグ&ドロップ用のコードを記述
- プロジェクトを実行し、任意の画像をウィンドウ内にドラッグ&ドロップして、画像が表示されたら完成
手順1
Visual Studioのソリューションエクスプローラーに"Main.storyboard"の項目があるので、右クリックして「プログラムから開く → Xcode Interface Builder」を選択し、Xcodeを開きます。
Xcodeの右上にある"+アイコン"(Library)を押し、CustomViewを検索してViewControllerScene内に配置します。
サイズや位置を変更して自由に配置した後、右側のユーティリティ・エリアに"Custom Class"の項目があるので、ここに任意のクラス名を記述します。
手順2
Visual Studioに戻ると自動的にXcodeと同期され、手順1で設定したクラスがソリューションエクスプローラーに追加されます。
ドラッグ&ドロップ用のコードを、追加されたクラスに記述していきます。
コードの全容は以下の通りです。
// This file has been autogenerated from a class added in the UI designer.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Foundation;
using AppKit;
using CoreGraphics;
namespace Drag_and_Drop
{
public partial class DropImageView : NSView
{
public DropImageView (IntPtr handle) : base (handle)
{
//初期化処理を呼ぶ
this.Initialize();
}
//初期化
private void Initialize()
{
//ファイルのドラッグ&ドロップを受付可能に
this.RegisterForDraggedTypes(new string[] { NSPasteboard.NSFilenamesType });
}
//ドラッグされたファイルのドロップ判定
public override NSDragOperation DraggingEntered(NSDraggingInfo sender)
{
//受け入れるファイルリスト
var files = this.GetFiles(sender);
//拡張子がpngで、1つのみドラッグしていた場合にドロップする
if (files.Length == 1)
{
//ドラッグしたファイルをコピーしてドロップ
return NSDragOperation.Copy;
}
return NSDragOperation.Generic;
}
//受け入れるファイルリストを取得
private string[] GetFiles(NSDraggingInfo info)
{
//受け入れるファイルリスト
var files = new List<string>();
var availableType = info.DraggingPasteboard.GetAvailableTypeFromArray(RegisteredDragTypes());
if (availableType == NSPasteboard.NSFilenamesType)
{
//ドラッグされてきたファイルリスト
var lists = info.DraggingPasteboard.GetPropertyListForType(NSPasteboard.NSFilenamesType);
if (lists is NSArray)
{
//対象の拡張子だったら受け入れる (今回は"png")
var file = lists as NSArray;
for (nuint i = 0; i < file.Count; i++)
{
var item = file.GetItem<NSString>(i);
var ext = Path.GetExtension(item).ToLower();
if (ext == ".png")
{
files.Add(item);
}
}
}
}
return files.ToArray();
}
//ドロップされたファイルの処理
public override bool PerformDragOperation(NSDraggingInfo sender)
{
//受け入れるファイルリスト
var files = this.GetFiles(sender);
//ファイルを1つだけドラッグしていた場合のみ受け入れる
if (files.Length != 1)
{
return false;
}
//ファイル情報を取得
var file = files[0];
var img = new NSImage(file);
//ウィンドウ内に画像を表示するため、画像サイズを調整
var imgResize = this.AspectFitSizeForMaxDimension(img);
//既に画像が表示されていた場合は、その画像を削除する
var drawImageViewChildren = this.Subviews;
foreach (var child in drawImageViewChildren)
{
if (child is NSImageView)
{
child.RemoveFromSuperview();
break;
}
}
//画像をウィンドウ内に貼り付ける (中央に表示されるように位置を設定)
var imgView = new NSImageView(new RectangleF(
(float)this.Frame.Size.Width / 2 - (float)imgResize.Width / 2, (float)this.Frame.Size.Height / 2 - (float)imgResize.Height / 2, (float)imgResize.Width, (float)imgResize.Height
))
{
Image = img
};
this.AddSubview(imgView);
return true;
}
private CGSize AspectFitSizeForMaxDimension(NSImage img)
{
//ウィンドウサイズ
var minSize = Math.Min(this.Frame.Size.Width, this.Frame.Size.Height);
float maxDimension = (float)minSize - 10.0f;
//画像サイズ
var imgWidth = (float)img.Size.Width;
var imgHeight = (float)img.Size.Height;
//ウィンドウサイズより画像が大きければ、比率を保ったまま画像を縮小させる
if (imgWidth > maxDimension || imgHeight > maxDimension)
{
if (imgWidth == imgHeight)
{
imgWidth = maxDimension;
imgHeight = maxDimension;
}
else if (imgWidth > imgHeight)
{
var aspectRatio = imgWidth / imgHeight;
imgWidth = maxDimension;
imgHeight = maxDimension / aspectRatio;
}
else if (imgWidth < imgHeight)
{
var aspectRatio = imgHeight / imgWidth;
imgWidth = maxDimension / aspectRatio;
imgHeight = maxDimension;
}
}
CGSize resize = new CGSize(imgWidth, imgHeight);
return resize;
}
}
}
手順3
プロジェクトを実行し、任意のpngファイルをドラッグしてウィンドウ上にドロップすると、画像が表示されます。
続けて他pngファイルをドラッグ&ドロップすると、新しい画像に更新されます。
まとめ
ドラッグ&ドロップ処理について、基本的な実装を紹介しました。
他にも複数画像をドラッグ&ドロップしてウィンドウ内にランダムに配置したり、ウィンドウ内の画像をドラッグして自由に移動させたりなど、色々応用が効きます。
今回の内容から発展させて試してみてくださいね。