async void OnTapGestureRecognizerTapped(object sender, EventArgs args) { if (_isInFront) { _contentHolder.RaiseChild(BackView); FrontView.Opacity = 0.2f; await Task.WhenAll( FrontView.FadeTo(1, 500), FrontView.TranslateTo(0, -translationPosition, 500), BackView.TranslateTo(0, 0, 500), BackView.ScaleTo(1, 500), FrontView.ScaleTo(0.8f, 500) ); } else { _contentHolder.RaiseChild(FrontView); BackView.Opacity = 0.2f; await Task.WhenAll( BackView.FadeTo(1, 500), FrontView.TranslateTo(0, 0, 500), BackView.TranslateTo(0, -translationPosition, 500), FrontView.ScaleTo(1, 500), BackView.ScaleTo(0.8f, 500) ); } _isInFront = !_isInFront; }
public void Flip(FlipDirection direction, NSAction completion) { if (flippingInProgress || FrontView == null || BackView == null) { return; } flippingInProgress = true; projectionLayer = CALayer.Create(); // this is to make the perspective and 2.5D effect projectionLayer.Frame = this.Layer.Bounds; var perspective = CATransform3D.Identity; float zDistanse = 350f; // change this to the actual perspective and distance of the projection plane perspective.m34 = 1f / -zDistanse; projectionLayer.SublayerTransform = perspective; this.Layer.AddSublayer(projectionLayer); frontImage = FrontView.GetImage(); backImage = BackView.GetImage(); topFaceLayer = new GradientLayer(GradientLayerType.Face, GradientLayerAreaType.Top); topFaceLayer.Frame = new RectangleF(0f, 0f, projectionLayer.Frame.Size.Width, (float)Math.Floor(projectionLayer.Frame.Size.Height / 2f)); bottomFaceLayer = new GradientLayer(GradientLayerType.Face, GradientLayerAreaType.Bottom); bottomFaceLayer.Frame = new RectangleF(0f, (float)Math.Floor(projectionLayer.Frame.Size.Height / 2f), projectionLayer.Frame.Size.Width, (float)Math.Floor(projectionLayer.Frame.Size.Height / 2f)); mainFlipLayer = new DoubleSidedLayer(); mainFlipLayer.AnchorPoint = new PointF(1f, 1f); mainFlipLayer.Frame = new RectangleF(0f, 0f, projectionLayer.Frame.Size.Width, (float)Math.Floor(projectionLayer.Frame.Size.Height / 2f)); mainFlipLayer.ZPosition = 1f; mainFlipLayer.FrontLayer = new GradientLayer(GradientLayerType.Flip, GradientLayerAreaType.Top); mainFlipLayer.BackLayer = new GradientLayer(GradientLayerType.Flip, GradientLayerAreaType.Bottom); // offscreen rendering optimization to reuse the offscreen buffer topFaceLayer.ShouldRasterize = true; bottomFaceLayer.ShouldRasterize = true; if (direction == FlipDirection.Down) { topFaceLayer.Contents = backImage.CGImage; bottomFaceLayer.Contents = frontImage.CGImage; mainFlipLayer.FrontLayer.Contents = frontImage.CGImage; mainFlipLayer.BackLayer.Contents = backImage.CGImage; topFaceLayer.GradientOpacity = 1f; mainFlipLayer.Transform = CATransform3D.Identity; } else { topFaceLayer.Contents = frontImage.CGImage; bottomFaceLayer.Contents = backImage.CGImage; mainFlipLayer.FrontLayer.Contents = backImage.CGImage; mainFlipLayer.BackLayer.Contents = frontImage.CGImage; bottomFaceLayer.GradientOpacity = 1f; mainFlipLayer.Transform = CATransform3D.MakeRotation((float)-Math.PI, 1f, 0f, 0f); } // Add layers to the projection layer, so we apply the projections to it projectionLayer.AddSublayer(topFaceLayer); projectionLayer.AddSublayer(bottomFaceLayer); projectionLayer.AddSublayer(mainFlipLayer); NSTimer.CreateScheduledTimer(0.01, delegate { CATransaction.Begin(); CATransaction.AnimationDuration = Duration; CATransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut); CATransaction.CompletionBlock = delegate { fv = this.FrontView; FrontView = BackView; BackView = fv; flippingInProgress = false; if (completion != null) { completion.Invoke(); } }; // this is the whole trick, change the angle in timing function float angle = (float)Math.PI * (1f - (float)direction); mainFlipLayer.Transform = CATransform3D.MakeRotation(angle, 1f, 0f, 0f); topFaceLayer.GradientOpacity = (float)direction; bottomFaceLayer.GradientOpacity = 1f - (float)direction; mainFlipLayer.FrontLayer.GradientOpacity = 1f - (float)direction; mainFlipLayer.BackLayer.GradientOpacity = (float)direction; CATransaction.Commit(); }); }