Статьи

Захват подписей на iPhone (iOS) с помощью MonoTouch

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

Опять же, я надеюсь, что вы найдете это полезным, и, пожалуйста, сообщите мне, если у вас есть какие-либо вопросы или комментарии