В предыдущем посте я продемонстрировал, как захватить подпись на устройствах 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; }
Опять же, я надеюсь, что вы найдете это полезным, и, пожалуйста, сообщите мне, если у вас есть какие-либо вопросы или комментарии