2013-09-24 55 views
4

我有一個很好的簽名控件,除非您有一個長名稱,然後開始顯示空白。似乎與性能有關,但在模擬器和最新的iPad上都是一樣的。我附上了一個示例項目以及簽名圖形代碼。在iOS中捕獲簽名時的性能問題

任何幫助將不勝感激!

https://dl.dropboxusercontent.com/u/25670071/SignatureArchive.zip

enter image description here

using System; 
using MonoTouch.UIKit; 
using MonoTouch.CoreGraphics; 
using System.Drawing; 

namespace MyApp 
{ 
    public class SignatureViewV2 : UIView 
    {  
    public delegate void SignatureChanged(); 
    public SignatureChanged OnSignatureChanged; 

    private bool _empty = true; 
    // clear the canvas 
    public void Clear() 
    { 
     drawPath.Dispose(); 
     drawPath = new CGPath(); 
     fingerDraw = false; 
     SetNeedsDisplay(); 
     _empty = true; 
    } 

    public bool IsEmpty() 
    { 
     return _empty; 
    } 

    public SignatureViewV2 (RectangleF frame) : base(frame) 
    { 

     this.drawPath = new CGPath(); 
     this.BackgroundColor = UIColor.White; 

    } 


    private PointF touchLocation; 
    private PointF prevTouchLocation; 
    private CGPath drawPath; 
    private bool fingerDraw; 

    public override void TouchesBegan (MonoTouch.Foundation.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); 
     this.SetNeedsDisplay(); 

    } 

    public override void Draw (RectangleF rect) 
    { 
     base.Draw (rect); 

     if (this.fingerDraw) { 
      using (CGContext context = UIGraphics.GetCurrentContext()) { 
       context.SetStrokeColor (UIColor.FromRGB(63, 112, 185).CGColor); 
       context.SetLineWidth (2f); 
       context.SetLineJoin (CGLineJoin.Round); 
       context.SetLineCap (CGLineCap.Round); 
       this.drawPath.MoveToPoint (this.prevTouchLocation); 
       this.drawPath.AddLineToPoint (this.touchLocation); 
       context.AddPath (this.drawPath); 
       context.DrawPath (CGPathDrawingMode.Stroke); 
      } 
      if(OnSignatureChanged != null) 
       OnSignatureChanged(); 
      _empty = false; 
     }  
    } 

    public override void TouchesMoved (MonoTouch.Foundation.NSSet touches, UIEvent evt) 
    { 
     base.TouchesMoved (touches, evt); 

     UITouch touch = touches.AnyObject as UITouch; 
     this.touchLocation = touch.LocationInView (this); 
     this.prevTouchLocation = touch.PreviousLocationInView (this); 
     this.SetNeedsDisplay(); 
    } 

    public UIImage GetDrawingImage() 
    { 
     UIImage returnImg = null; 

     UIGraphics.BeginImageContext (this.Bounds.Size); 

     using (CGContext context = UIGraphics.GetCurrentContext()) { 
      context.SetStrokeColor (UIColor.FromRGB(63, 112, 185).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; 
    } 



} 

}

+0

我認爲這是可能的表現有關。我能夠在視網膜iPad上重現您的問題,但不是正常的問題。 –

回答

2

已經嘗試了所有的答案後,使這些點,我決定使用在提供一個好得多的簽名貝塞爾曲線轉換成Objective-C的例子我意見。此代碼是從這個優秀的職位採取了主題: http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawing/

public class SignatureViewV3 : UIView 
{  

    public delegate void SignatureChanged(); 

    public SignatureChanged OnSignatureChanged; 
    private bool _empty = true; 


    UIBezierPath path; 

    UIImage incrementalImage; 

    PointF[] pts = new PointF[5]; 

    uint ctr; 

    [Export ("initWithFrame:")] 

    public SignatureViewV3 (RectangleF rect): base(rect) 

    { 

     this.MultipleTouchEnabled = false; 

     this.BackgroundColor = UIColor.Clear; 

     path = new UIBezierPath(); 

     path.LineWidth = 2; 

    } 
    public bool IsEmpty() 
    { 
     return incrementalImage == null && ctr == 0; 
    } 
    public void Clear() 
    { 
     if(incrementalImage != null) 
     { 
      incrementalImage.Dispose(); 
      incrementalImage = null; 
     } 
     path.RemoveAllPoints(); 
     SetNeedsDisplay(); 

    } 

    [Export("initWithCoder:")] 

    public SignatureViewV3 (NSCoder coder) : base(coder) 

    { 

     this.MultipleTouchEnabled = false; 

     this.BackgroundColor = UIColor.Clear; 

     path = new UIBezierPath(); 

     path.LineWidth = 2; 

    } 

    public override void Draw (RectangleF rect) 
    { 

     if (incrementalImage != null) 
      incrementalImage.Draw(rect); 

     path.Stroke(); 

    } 

    public override void TouchesBegan (NSSet touches, UIEvent evt) 

    { 


     ctr = 0; 

     UITouch touch = touches.AnyObject as UITouch; 

     pts[0] = touch.LocationInView(this); 

    } 

    public override void TouchesMoved (NSSet touches, UIEvent evt) 
    { 
     if(OnSignatureChanged != null) 
      OnSignatureChanged(); 

     UITouch touch = touches.AnyObject as UITouch; 

     PointF p = touch.LocationInView(this); 

     ctr++; 

     pts[ctr] = p; 


     if (ctr == 3) 
     { 
      pts[2] = new PointF((pts[1].X + pts[3].X)/2.0f, (pts[1].Y + pts[3].Y)/2.0f); 
      path.MoveTo(pts[0]); 
      path.AddQuadCurveToPoint (pts [2], pts [1]); 

      this.SetNeedsDisplay(); 
      pts[0] = pts[2]; 
      pts[1] = pts[3]; 
      ctr = 1; 
     } 

    } 

    public override void TouchesEnded (NSSet touches, UIEvent evt) 

    { 

     if (ctr == 0) // only one point acquired = user tapped on the screen 
     { 
      path.AddArc (pts [0], path.LineWidth/2, 0, (float)(Math.PI * 2), true); 
     } 
     else if (ctr == 1) 
     { 
      path.MoveTo (pts [0]); 
      path.AddLineTo (pts [1]); 
     } 
     else if (ctr == 2) 
     { 
      path.MoveTo (pts [0]); 
      path.AddQuadCurveToPoint (pts [2], pts [1]); 
     } 

     this.drawBitmap(); 
     this.SetNeedsDisplay(); 


     path.RemoveAllPoints(); 

     ctr = 0; 

    } 

    public override void TouchesCancelled (NSSet touches, UIEvent evt) 

    { 

     this.TouchesEnded(touches, evt); 

    } 
    public UIImage GetDrawingImage() 
    { 
     UIGraphics.BeginImageContextWithOptions(this.Bounds.Size, false, 0); 

     if(incrementalImage == null) 
     { 
      incrementalImage = new UIImage(); 
      UIBezierPath rectPath = UIBezierPath.FromRect(this.Bounds); 
      UIColor.Clear.SetFill(); 
      rectPath.Fill(); 
     } 

     incrementalImage.Draw(new PointF(0,0)); 

     UIColor.Black.SetStroke(); 

     path.Stroke(); 

     incrementalImage = UIGraphics.GetImageFromCurrentImageContext(); 

     UIGraphics.EndImageContext(); 
     return incrementalImage; 
    } 
    public void drawBitmap() 
    { 
     UIGraphics.BeginImageContextWithOptions(this.Bounds.Size, false, 0); 

     if(incrementalImage == null) 
     { 
      incrementalImage = new UIImage(); 
      UIBezierPath rectPath = UIBezierPath.FromRect(this.Bounds); 
      UIColor.Clear.SetFill(); 
      rectPath.Fill(); 
     } 

     incrementalImage.Draw(new PointF(0,0)); 

     UIColor.Black.SetStroke(); 

     path.Stroke(); 

     incrementalImage = UIGraphics.GetImageFromCurrentImageContext(); 

     UIGraphics.EndImageContext(); 

    } 


} 
3

你不能依靠Draw()被要求每TouchesMoved()。如果每2次觸摸都會調用Draw(),則會得到如所述的間隙。

我會解決,通過在TouchesMoved()排隊(例如,在Queue<T>)的觸摸和Draw()

離隊你可能也有另外一個問題:在每個Draw(),你重新添加的完整路徑每次當前的路徑。你可能只需要調用AddPath來解決這個問題,或者調用AddPath()一次,爲你的路徑添加段(`Move,AddLine)並重新繪製它。但我還沒有測試過這些。

+0

不錯!感謝一堆:-) –

+0

你實施了哪個部分?隊列 ? –

+0

我嘗試了Queue,並在touchesBegan和touchesMoved中添加了drawPath。它既可以選擇,也可以不適合繪圖應用程序,因爲它在繪製一段時間後開始滯後(但沒有間隙)。 –

1

在我們的一些內部應用程序用戶升級到iOS 7後,我遇到了完全相同的問題。我曾嘗試使用隊列和貝塞爾曲線代替連接觸點,但最終切換到使用OpenGL我的實施。

我已經在這裏找到了一個十分實用指南:Capture a Signature on iOS 和Github上的Objective-C的項目:Signature Demo

我花了一天重寫它在C#和適應它,因爲我在我的應用程序使用在Obj-C中不太好,但它確實工作得很好。

類代碼可以在這裏(GLSignatureView類):Github

+0

我試過你的代碼,稍微修改它以允許更粗的行(認爲你選擇了一條非常細的行?),它的效果很好。謝謝! –

0

我曾在這個問題中描述的完全一樣的問題。我查看了上面@Dmitry的答案,但是與我的安靜不同,它需要很多更改。所以我遵循上面的@Stephane建議,只是做了MoveTouches的排隊,完美運作。多謝你們。

我正在把我的解決方案放在這裏,以防其他人需要它。請注意,我正在捕獲簽名點而不是圖像簽名。我們有另一種算法用於使用不同的設置

using MonoTouch.CoreGraphics; 
using MonoTouch.UIKit; 
using System.Drawing; 
using System; 
using Leopard.Interfaces; 
using MonoTouch.Foundation; 
using Leopard.Mobile.Core.Signature; 
using Leopard.Mobile.Core.Drawing; 
using Leopard.Interfaces.Drawing; 
using Leopard.Interfaces.Screens.Controls; 
using System.Linq; 
using System.Collections.Concurrent; 

namespace Leopard.Mobile.Controls 
{ 
    public class SignatureView : LeopardControlBase, ISignatureView 
    { 
     public SignatureView (RectangleF frame) : base(frame) 
     { 
      base.Frame = frame; 
      ViewFrame = new LeopardFrame { 
       X = (int)frame.X, 
       Y = (int) frame.Y, 
       Width = frame.Width, 
       Height = frame.Height 
      }; 
      _DrawPath = new CGPath(); 
      SetupAppearance(); 
      _ScalingFactor = new LeopardFrame { Width = 1, Height = 1 }; 
      DrawWatermarks(); 
     } 

    public void Initialise(int penWidth, WatermarkSettings watermarks, string backgroundImageFileName) 
    { 
     PenWidth = penWidth; 
     Watermarks = watermarks; 
     BackgroundImageFileName = backgroundImageFileName; 

     var dimensions = new LeopardFrame 
     { 
      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); 
     this.SetNeedsDisplay(); 

     _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; 
      _SignatureData.AddPoint(SignatureState.End, (int)this._TouchLocation.X, (int)this._TouchLocation.Y); 
     } 
    } 

    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) 
       { 
        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 void Add(IControl control) 
    { 
     var view = control as UIView; 
     if (view != null) 
     { 
      EnsureAddingWatermarkControl(view); 
     } 
    } 

    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 ILeopardFrame _ScalingFactor; 
    private SignatureData _SignatureData { get; set; } 

    public SignatureData SignatureData { get { return _SignatureData; } } 
    public event SignatureFailedWithExceptionHandler SignatureFailedWithException; 
    public string BackgroundImageFileName {get;set;} 
    public int PenWidth { get; set; } 
    public WatermarkSettings Watermarks {get;set;} 
    public ILeopardFrame ViewFrame { get; set; } 

    private void OnFailedWithException(Exception exception) 
    { 
     if (SignatureFailedWithException != null) 
     { 
      SignatureFailedWithException(exception); 
     } 
    } 

    private void EnsureAddingWatermarkControl(UIView view) 
    { 
     var existingView = this.Subviews.ToList().FirstOrDefault( v => v is IControl && 
                   v.Frame.X == view.Frame.X && 
                   v.Frame.Y == view.Frame.Y); 
     if (existingView != null) 
     { 
      existingView.RemoveFromSuperview(); 
      existingView.Dispose(); 
     } 
     this.AddSubview(view); 
    } 

    private void DrawWatermarks() 
    { 
     if (Watermarks != null) 
     { 
      Watermarks.DrawWatermarks(this, _ScalingFactor); 
     } 
    } 

    private void SetupAppearance() 
    { 
     BackgroundColor = UIColor.White; 
     Layer.BorderWidth = 5f; 
     Layer.BorderColor = UIColor.FromRGB ( Constants.LeopardBackgroundColors.Red, 
               Constants.LeopardBackgroundColors.Green, 
               Constants.LeopardBackgroundColors.Blue 
              ).CGColor; 
    } 

    #endregion 
} 

public class TouchsQueue 
{ 
    public PointF TouchLocation {get;set;} 
    public PointF PrevTouchLocation { get; set; } 
} 

}

+0

你可以分享請渲染算法,我需要爲android和ios編寫簽名應用程序,並在服務器上縮放簽名。 –