В предыдущем посте я продемонстрировал, как захватить подпись на устройствах Android. В этом посте мы рассмотрим, как сделать то же самое на устройствах iOS (iPhone, iPad и т. Д.).
По сути, идея очень проста: мы создаем собственное представление и прислушиваемся к прикосновениям к этому представлению. Это включает в себя TouchBegins (), TouchMoved () и TouchEnds (). Затем с каждым прикосновением мы обновляем наш список точек. В другом предыдущем посте я показал, как структурировать массив из двух тусклых точек в строку. Затем используйте это, чтобы восстановить подпись на стороне сервера. Однако мы могли бы также захватить подпись в виде изображения на мобильном устройстве и передать ее на сервер, если вы хотите получить изображение напрямую, а не в списке точек.
Код ниже показывает SignatureView.
using MonoTouch.CoreGraphics;
using MonoTouch.UIKit;
using System.Drawing;
using System;
using MyApp.Interfaces;
using MonoTouch.Foundation;
using MyApp.Core.Signature;
using MyApp.Core.Drawing;
using MyApp.Interfaces.Drawing;
using MyApp.Interfaces.Screens.Controls;
using System.Linq;
using System.Collections.Concurrent;
namespace MyApp.Controls
{
public class SignatureView : UIControl, ISignatureView
{
public SignatureView (RectangleF frame) : base(frame)
{
base.Frame = frame;
ViewFrame = new MyFrame {
X = (int)frame.X,
Y = (int)frame.Y,
Width = frame.Width,
Height = frame.Height
};
_DrawPath = new CGPath();
SetupAppearance();
_ScalingFactor = new MyFrame { Width = 1, Height = 1 };
DrawWatermarks();
}
public void Initialise(int penWidth, WatermarkSettings watermarks, string backgroundImageFileName)
{
PenWidth = penWidth;
Watermarks = watermarks;
BackgroundImageFileName = backgroundImageFileName;
var dimensions = new MyFrame
{
Width = Frame.Width,
Height = Frame.Height
};
_SignatureData = new SignatureData(dimensions, _ScalingFactor, watermarks);
}
public void Clear ()
{
_DrawPath.Dispose();
_DrawPath = new CGPath();
_FingerDraw = false;
_TouchLocation = new PointF(0, 0);
_PrevTouchLocation = new PointF(0, 0);
SetNeedsDisplay();
_SignatureData.Clear();
DrawWatermarks();
_TouchsQueue = new ConcurrentQueue<TouchsQueue>();
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
UITouch touch = touches.AnyObject as UITouch;
this._FingerDraw = true;
this._TouchLocation = touch.LocationInView (this);
this._PrevTouchLocation = touch.PreviousLocationInView (this);
_SignatureData.AddPoint(SignatureState.Start, (int)this._TouchLocation.X, (int)this._TouchLocation.Y);
}
public override void TouchesEnded(NSSet touches, UIEvent e)
{
base.TouchesEnded(touches, e);
if (this._FingerDraw)
{
UITouch touch = touches.AnyObject as UITouch;
_TouchLocation = touch.LocationInView(this);
_PrevTouchLocation = touch.PreviousLocationInView(this);
_FingerDraw = false;
_TouchsQueue.Enqueue(new TouchsQueue {TouchLocation = _TouchLocation, PrevTouchLocation = _PrevTouchLocation });
_SignatureData.AddPoint(SignatureState.End, (int)this._TouchLocation.X, (int)this._TouchLocation.Y);
this.SetNeedsDisplay ();
}
}
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
base.TouchesMoved (touches, evt);
UITouch touch = touches.AnyObject as UITouch;
_TouchLocation = touch.LocationInView(this);
_PrevTouchLocation = touch.PreviousLocationInView(this);
_TouchsQueue.Enqueue(new TouchsQueue {TouchLocation = _TouchLocation, PrevTouchLocation = _PrevTouchLocation });
_SignatureData.AddPoint(SignatureState.Move, (int)this._TouchLocation.X, (int)this._TouchLocation.Y);
SetNeedsDisplay();
}
public override void Draw (RectangleF rect)
{
base.Draw (rect);
if (_DrawPath != null)
{
using (CGContext context = UIGraphics.GetCurrentContext())
{
if (context != null)
{
DrawBackgroundImage(context);
DrawSignatureLines(context);
}
}
}
}
private void DrawSignatureLines(CGContext context)
{
TouchsQueue queueElement = null;
while(_TouchsQueue.TryDequeue(out queueElement))
{
if (queueElement != null)
{
context.SetStrokeColor(UIColor.Black.CGColor);
context.SetLineWidth(PenWidth);
context.SetLineJoin(CGLineJoin.Round);
context.SetLineCap(CGLineCap.Round);
_DrawPath.MoveToPoint(queueElement.PrevTouchLocation);
_DrawPath.AddLineToPoint(queueElement.TouchLocation);
context.AddPath(_DrawPath);
context.DrawPath(CGPathDrawingMode.Stroke);
}
}
}
public string GetSignatureData()
{
var result = string.Empty;
if (_SignatureData != null)
{
try
{
result = _SignatureData.ExtractAsString();
}
catch (Exception exception)
{
OnFailedWithException(exception);
}
}
return result;
}
#region Implementation
private PointF _TouchLocation;
private PointF _PrevTouchLocation;
private CGPath _DrawPath;
private bool _FingerDraw;
private ConcurrentQueue<TouchsQueue> _TouchsQueue = new ConcurrentQueue<TouchsQueue>();
private IMyFrame _ScalingFactor;
private SignatureData _SignatureData { get; set; }
private UIImage _BackgroundImage;
public SignatureData SignatureData { get { return _SignatureData; } }
public event SignatureFailedWithExceptionHandler SignatureFailedWithException;
public string BackgroundImageFileName { get; set; }
public int PenWidth { get; set; }
public IMyFrame BackgroundImageFrame { get; set; }
public WatermarkSettings Watermarks {get;set;}
public IMyFrame ViewFrame { get; set; }
private void OnFailedWithException(Exception exception)
{
if (SignatureFailedWithException != null)
{
SignatureFailedWithException(exception);
}
}
private void SetupAppearance ()
{
BackgroundColor = UIColor.White;
Layer.BorderWidth = 5f;
Layer.BorderColor = UIColor.Orange;
}
#endregion
}
public class TouchsQueue
{
public PointF TouchLocation {get;set;}
public PointF PrevTouchLocation { get; set; }
}
}
Главное, на что следует обратить внимание, это то, что после окончания каждого сенсорного события мы аннулируем представление, чтобы приложение вызывало метод Draw (). Затем в методе Draw () мы используем наш двумерный список точек и рисуем подпись.
Вы могли заметить, что я также рисую водяные знаки, но это отдельная тема, о которой я постараюсь рассказать в другой раз.
Другое дело, что у нас есть TouchQueue. Это используется потому, что iOS иногда защищенно пропускает некоторые запросы, чтобы лишить законной силы (перерисовать) представление, если есть несколько касаний в короткий промежуток времени. Это приведет к тому, что строка подписи будет сегментирована с промежутками между ними.
Если вы предпочитаете получать изображение напрямую, а не собирать изображения и передавать их на сервер, вы можете легко сделать это с помощью графического контекста представления, как показано ниже:
public UIImage GetDrawingImage ()
{
UIImage returnImg = null;
UIGraphics.BeginImageContext (this.Bounds.Size);
using (CGContext context = UIGraphics.GetCurrentContext()) {
context.SetStrokeColor (UIColor.Black.CGColor);
context.SetLineWidth (5f);
context.SetLineJoin (CGLineJoin.Round);
context.SetLineCap (CGLineCap.Round);
context.AddPath (this._DrawPath);
context.DrawPath (CGPathDrawingMode.Stroke);
returnImg = UIGraphics.GetImageFromCurrentImageContext ();
}
UIGraphics.EndImageContext ();
return returnImg;
}
Опять же, я надеюсь, что вы найдете это полезным, и, пожалуйста, сообщите мне, если у вас есть какие-либо вопросы или комментарии