SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, iOS Chart, Android Chart and JavaScript Chart Components
Please note! These examples are new to SciChart Mobile v3 release! SciChart iOS & Android ship with Xamarin.iOS and Xamarin.Android bindings around our native iOS & Android Chart controls, allowing you to create fast & feature rich charts to your Xamarin applications. We include ~90 native iOS examples and 90 Android examples, but now also ~60 Xamarin Chart Examples to help you get started with SciChart. You can download the source for our Xamarin Chart Examples from Github, or browse the source code below.
This example showcases how to create a custom gesture-based chart modifier with help of Custom Chart Modifier API using SciChart Xamarin.iOS and Xamarin.Android.
The C#/Xamarin.iOS/Xamarin.Android source code for the Xamarin Custom Gesture Modifier example is included below (Scroll down!).
Did you know you can also view the source code from one of the following sources as well?
using System;
using Foundation;
using UIKit;
using CoreGraphics;
using ObjCRuntime;
using SciChart.iOS.Charting;
namespace Xamarin.Examples.Demo.iOS
{
public class CustomGestureModifier : SCIGestureModifierBase
{
private DoubleTouchDownGestureRecognizer doubleTapGesture;
private CGPoint InitialLocation = CGPoint.Empty;
private readonly nfloat ScaleFactor = -50;
private bool CanPan;
public CustomGestureModifier() { }
public override void AttachTo(IISCIServiceContainer services)
{
base.AttachTo(services);
doubleTapGesture = new DoubleTouchDownGestureRecognizer(this, new Selector("handleDoubleTapGesture:"));
if (ParentSurface is UIView)
{
UIView surfaceView = (UIView)ParentSurface;
surfaceView.AddGestureRecognizer(doubleTapGesture);
}
}
protected override UIGestureRecognizer CreateGestureRecognizer()
{
return new UIPanGestureRecognizer();
}
protected override void InternalHandleGesture(UIGestureRecognizer gestureRecognizer)
{
if (!CanPan) return;
UIPanGestureRecognizer gesture = (UIPanGestureRecognizer)gestureRecognizer;
UIView parentView = ParentSurface.ModifierSurface.View;
switch (gesture.State)
{
case UIGestureRecognizerState.Began:
InitialLocation = gestureRecognizer.LocationInView(parentView);
break;
case UIGestureRecognizerState.Changed:
performZoom(InitialLocation, gesture.TranslationInView(parentView).Y);
gesture.SetTranslation(CGPoint.Empty, parentView);
break;
default:
CanPan = false;
break;
}
}
void performZoom(CGPoint point, nfloat yScaleFactor)
{
nfloat fraction = yScaleFactor / ScaleFactor;
for (int i = 0; i < XAxes.Count; i++)
{
growAxis(XAxes[i], point, fraction);
}
}
void growAxis(IISCIAxis axis, CGPoint point, nfloat fraction)
{
nfloat width = axis.LayoutSize.Width;
nfloat coord = width - point.X;
double minFraction = (coord / width) * fraction;
double maxFraction = (1 - coord / width) * fraction;
axis.ZoomByFractionMin(minFraction, maxFraction);
}
[Export("handleDoubleTapGesture:")]
void handleDoubleTap(UILongPressGestureRecognizer gesture)
{
if (gesture.State == UIGestureRecognizerState.Ended)
{
CanPan = true;
}
}
}
}
using CoreGraphics;
using Xamarin.Examples.Demo.Data;
using SciChart.iOS.Charting;
using UIKit;
namespace Xamarin.Examples.Demo.iOS
{
[ExampleDefinition("Custom Gesture Modifier Chart", description: "Shows how to create a Custom Gesture Modifier", icon: ExampleIcon.Impulse)]
public class CustomGestureModifierChartViewController : SingleChartViewController<SCIChartSurface>
{
protected override void InitExample()
{
var xAxis = new SCINumericAxis { GrowBy = new SCIDoubleRange(0.1, 0.1) };
var yAxis = new SCINumericAxis { GrowBy = new SCIDoubleRange(0.1, 0.1) };
var ds1Points = DataManager.Instance.GetDampedSinewave(1.0, 0.05, 50, 5);
var dataSeries = new XyDataSeries<double, double>();
dataSeries.Append(ds1Points.XData, ds1Points.YData);
var rSeries = new SCIFastImpulseRenderableSeries
{
DataSeries = dataSeries,
StrokeStyle = new SCISolidPenStyle(0xFF0066FF, 2f),
PointMarker = new SCIEllipsePointMarker
{
Size = new CGSize(7, 7),
StrokeStyle = new SCISolidPenStyle(0xFF0066FF, 2f),
FillStyle = new SCISolidBrushStyle(0xFF0066FF),
}
};
var annotation = new SCITextAnnotation
{
Text = "Double Tap and pan vertically to Zoom In/Out. \nDouble tap to Zoom Extents.",
FontStyle = new SCIFontStyle(16, UIColor.White),
X1Value = 0.5,
Y1Value = 0,
CoordinateMode = SCIAnnotationCoordinateMode.Relative,
VerticalAnchorPoint = SCIVerticalAnchorPoint.Top,
HorizontalAnchorPoint = SCIHorizontalAnchorPoint.Center
};
using (Surface.SuspendUpdates())
{
Surface.XAxes.Add(xAxis);
Surface.YAxes.Add(yAxis);
Surface.RenderableSeries.Add(rSeries);
Surface.Annotations.Add(annotation);
Surface.ChartModifiers = new SCIChartModifierCollection { new CustomGestureModifier(), new SCIZoomExtentsModifier() };
SCIAnimations.WaveSeries(rSeries, 3, new SCICubicEase());
}
}
}
}
using System.Linq;
using UIKit;
using ObjCRuntime;
using Foundation;
namespace Xamarin.Examples.Demo.iOS
{
public class DoubleTouchDownGestureRecognizer : UITapGestureRecognizer
{
public DoubleTouchDownGestureRecognizer(NSObject target, Selector action) : base(target, action)
{
NumberOfTapsRequired = 2;
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
var firstTouch = (UITouch)touches.First();
if (firstTouch.TapCount != 2) return;
if (State == UIGestureRecognizerState.Possible)
{
State = UIGestureRecognizerState.Recognized;
}
}
public override void TouchesMoved(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Failed;
}
public override void TouchesEnded(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Failed;
}
}
}
using Android.Views.Animations;
using SciChart.Charting.Model.DataSeries;
using SciChart.Charting.Modifiers;
using SciChart.Charting.Visuals;
using SciChart.Charting.Visuals.Animations;
using SciChart.Charting.Visuals.Axes;
using SciChart.Charting.Visuals.PointMarkers;
using SciChart.Charting.Visuals.RenderableSeries;
using SciChart.Data.Model;
using SciChart.Drawing.Common;
using Xamarin.Examples.Demo.Data;
using Xamarin.Examples.Demo.Droid.Extensions;
using Xamarin.Examples.Demo.Droid.Fragments.Base;
using Android.Views;
using SciChart.Core.Utility.Touch;
using SciChart.Core;
using Android.Graphics;
using System;
using SciChart.Charting.Visuals.Annotations;
namespace Xamarin.Examples.Demo.Droid.Fragments.Examples
{
[ExampleDefinition("Custom Gesture Modifier", description: "Shows how to create custom gesture modifier", icon: ExampleIcon.Impulse)]
class CreateCustomGestureModifierFragment : ExampleBaseFragment
{
public SciChartSurface Surface => View.FindViewById<SciChartSurface>(Resource.Id.chart);
public override int ExampleLayoutId => Resource.Layout.Example_Single_Chart_Fragment;
protected override void InitExample()
{
var xAxis = new NumericAxis(Activity) { GrowBy = new DoubleRange(0.1, 0.1) };
var yAxis = new NumericAxis(Activity) { GrowBy = new DoubleRange(0.1, 0.1) };
var ds1Points = DataManager.Instance.GetDampedSinewave(1.0, 0.05, 50, 5);
var dataSeries = new XyDataSeries<double, double>();
dataSeries.Append(ds1Points.XData, ds1Points.YData);
var rSeries = new FastImpulseRenderableSeries
{
DataSeries = dataSeries,
StrokeStyle = new SolidPenStyle(0xFF0066FF, 2f.ToDip(Activity)),
PointMarker = new EllipsePointMarker
{
Width = 10.ToDip(Activity),
Height = 10.ToDip(Activity),
StrokeStyle = new SolidPenStyle(0xFF0066FF, 2f.ToDip(Activity)),
FillStyle = new SolidBrushStyle(0xFF0066FF)
}
};
var annotation = new TextAnnotation(Activity)
{
Text = "Double Tap and pan vertically to Zoom In/Out. \nDouble tap to Zoom Extents.",
X1Value = 0.5,
Y1Value = 0,
CoordinateMode = AnnotationCoordinateMode.Relative,
VerticalAnchorPoint = VerticalAnchorPoint.Top,
HorizontalAnchorPoint = HorizontalAnchorPoint.Center
};
using (Surface.SuspendUpdates())
{
Surface.XAxes.Add(xAxis);
Surface.YAxes.Add(yAxis);
Surface.RenderableSeries.Add(rSeries);
Surface.Annotations.Add(annotation);
Surface.ChartModifiers.Add(new CustomGestureModifier());
new WaveAnimatorBuilder(rSeries) { Interpolator = new DecelerateInterpolator(), Duration = 3000, StartDelay = 350 }.Start();
}
}
}
class CustomGestureModifier : GestureModifierBase
{
private bool _isScrolling = false;
private bool _isZoomEnabled = false;
private float _touchSlop;
private readonly PointF _start = new PointF();
private float _lastY;
public override void AttachTo(IServiceContainer services)
{
base.AttachTo(services);
var context = Context;
if (context == null) return;
this._touchSlop = ViewConfiguration.Get(context).ScaledTouchSlop * 2;
}
public override bool OnDoubleTap(MotionEvent e)
{
_start.Set(e.GetX(), e.GetY());
_lastY = e.GetY();
_isZoomEnabled = true;
return true;
}
public override void OnTouch(ModifierTouchEventArgs args)
{
base.OnTouch(args);
var motionEvent = args.E;
if (_isZoomEnabled && motionEvent.Action == MotionEventActions.Move)
{
OnScrollInYDirection(motionEvent.GetY());
}
}
private void OnScrollInYDirection(float y)
{
var distance = Math.Abs(y - _start.Y);
if (distance < _touchSlop || Math.Abs(y - _lastY) < 1f) return;
_isScrolling = true;
var prevDistance = Math.Abs(_lastY - _start.Y);
var diff = prevDistance > 0 ? distance / prevDistance - 1 : 0;
GrowBy(_start, XAxis, diff);
_lastY = y;
}
// zoom axis relative to the start point using fraction
private void GrowBy(PointF point, IAxis axis, double fraction)
{
var size = axis.AxisViewportDimension;
var coord = size - point.Y;
double minFraction = (coord / size) * fraction;
double maxFraction = (1 - coord / size) * fraction;
axis.ZoomBy(minFraction, maxFraction);
}
protected override void OnUp(MotionEvent e)
{
// need to disable zoom after finishing scrolling
if (_isScrolling)
{
_isScrolling = _isZoomEnabled = false;
_start.Set(float.NaN, float.NaN);
_lastY = float.NaN;
}
}
}
}