Instagram在应用程序某些部分的用户之间切换时使用3D旋转,如下所示。
我一直在尝试使用Xamarin Forms
但是无法在Xamarin上重新创建它。在Xamarin Forms上进行全屏旋转的正确技术是什么?
答案 0 :(得分:1)
你必须求助于自定义渲染器并在C#中实现类似的东西:https://github.com/katleta3000/RotateController(这个例子在Swift for iOS中,也可能类似于Android的类似)。
据我所知,纯Xamarin表格不可能。
这就是C#中提供的示例的样子(注意:我没有测试结果,因此以下代码中有两个TODO):
// Example Swift code licensed by MIT license. Converted to C#.
// Original author: katleta3000
using System;
using CoreAnimation;
using UIKit;
namespace MyProject.iOS
{
public enum RotationDirection : int
{
Right = 0,
Left
}
public enum RotationType : int
{
Push = 0,
Pop
}
class FCBlackBackgroundView : UIView
{
}
class FCRotateLayer : CALayer
{
}
public class RotatingController : UINavigationController
{
public void PushViewController(UIViewController controller, RotationDirection rotateDirection) {
Perform3DRotate(RotationType.Push, rotateDirection, controller);
}
public void PopViewController(RotationDirection rotateDirection) {
Perform3DRotate(RotationType.Pop, rotateDirection, null);
}
// MARK: - private
private void Perform3DRotate(RotationType type, RotationDirection rotateDirection, UIViewController controller) {
var layer = RotationLayer();
var cube = CubeTransform(rotateDirection, layer: layer);
if (type == RotationType.Push) {
this.PushViewController(controller, animated: false);
} else if (type == RotationType.Pop) {
this.PopViewController(animated: false);
}
layer.AddSublayer(LayerFromView(this.View, transform: cube));
this.View.AddSubview(BackgroundView(UIColor.White));
this.View.Layer.AddSublayer(layer);
layer.AddAnimation(RotationAnimation(rotateDirection), "rotate");
}
private CATransform3D CubeTransform(RotationDirection rotateDirection, CALayer layer)
{
var cube = CATransform3D.MakeTranslation(0, 0, 0);
layer.AddSublayer(LayerFromView(this.View, transform: cube));
cube.Rotate(Radians(90), 0, 1, 0);
cube.Translate(CubeSize(), 0, 0);
if (rotateDirection == RotationDirection.Left) {
cube.Rotate(Radians(90), 0, 1, 0);
cube.Translate(CubeSize(), 0, 0);
cube.Rotate(Radians(90), 0, 1, 0);
cube.Translate(CubeSize(), 0, 0);
}
return cube;
}
private FCRotateLayer RotationLayer()
{
FCRotateLayer layer = new FCRotateLayer();
layer.Frame = this.View.Frame;
layer.AnchorPoint = new CoreGraphics.CGPoint(0.5, 0.5);
CATransform3D transform = CATransform3D.Identity;
transform.m34 = 1.0f / -750;
layer.SublayerTransform = transform;
return layer;
}
private CAAnimation RotationAnimation(RotationDirection direction)
{
CATransaction.Flush();
var animationGroup = new CAAnimationGroup();
animationGroup.Duration = 0.4;
CABasicAnimation rotation = null;
CABasicAnimation translationX = null;
if (direction == RotationDirection.Right) {
rotation = CABasicAnimation.FromKeyPath("sublayerTransform.rotation.y");
rotation.To = FromObject(Radians(-90));
translationX = CABasicAnimation.FromKeyPath("sublayerTransform.translation.x");
translationX.To = FromObject(-TranslationForAnimation());
}
else if (direction == RotationDirection.Left) {
rotation = CABasicAnimation.FromKeyPath("sublayerTransform.rotation.y");
rotation.To = FromObject(Radians(90));
translationX = CABasicAnimation.FromKeyPath("sublayerTransform.translation.x");
translationX.To = FromObject(TranslationForAnimation());
}
var translationZ = CABasicAnimation.FromKeyPath("sublayerTransform.translation.z");
translationZ.To = FromObject(-TranslationForAnimation());
animationGroup.Animations = new CAAnimation[] { rotation, translationX, translationZ };
animationGroup.FillMode = CoreAnimation.CAFillMode.Forwards; //TODO: maybe convert nsstring to string
animationGroup.RemovedOnCompletion = false;
//TODO: unsubscribe necessary?
animationGroup.AnimationStopped += (sender, e) =>
{
var layers = this.View.Layer.Sublayers;
if (layers != null) {
foreach (var layer in layers) {
if (layer is FCRotateLayer) {
layer.RemoveFromSuperLayer();
}
}
}
foreach(var view in this.View.Subviews) {
if (view is FCBlackBackgroundView) {
view.RemoveFromSuperview();
}
}
};
animationGroup.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut);
return animationGroup;
}
private CALayer LayerFromView(UIView view) {
var rect = CoreGraphics.CGRect.FromLTRB(0, 0, this.View.Bounds.Size.Width, this.View.Frame.Size.Height);
CALayer imageLayer = new CALayer();
imageLayer.AnchorPoint = new CoreGraphics.CGPoint(1, 1);
imageLayer.Frame = rect;
UIGraphics.BeginImageContextWithOptions(view.Frame.Size, false, UIScreen.MainScreen.Scale);
var context = UIGraphics.GetCurrentContext();
if (context != null) {
view.Layer.RenderInContext(context);
}
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
imageLayer.Contents = image.CGImage;
return imageLayer;
}
private CALayer LayerFromView(UIView view, CATransform3D transform) {
var layer = LayerFromView(view);
layer.Transform = transform;
return layer;
}
private FCBlackBackgroundView BackgroundView(UIColor color)
{
var view = new FCBlackBackgroundView
{
Frame = this.View.Frame
};
view.BackgroundColor = color;
return view;
}
private float Radians(float degrees) {
return degrees * (float)Math.PI / 180;
}
private nfloat TranslationForAnimation() {
return CubeSize() / 2;
}
private nfloat CubeSize() {
return UIScreen.MainScreen.Bounds.Width;
}
}
}
答案 1 :(得分:0)
这是链接的swift控制器的C#版本。我无法从Swift翻译的一些东西。如果有人可以验证/思考数字转换,那就太好了。
using CoreAnimation;
using CoreGraphics;
using UIKit;
namespace hackettyHack
{
public enum RotationDirection //int
{
// case Right = 0, Left
Right,
Left
}
public enum RotationType //int
{
// case Push = 0, Pop }
Push,
Pop
}
class FCBlackBackgroundView : UIView
{
}
class FCRotateLayer : CALayer
{
}
public class RotatingController : UINavigationController
{
public void PushViewController(UIViewController controller, RotationDirection rotateDirection)
{
Perform3DRotate(RotationType.Push, rotateDirection, controller);
}
public void PopViewController(UIViewController controller, RotationDirection rotateDirection)
{
Perform3DRotate(RotationType.Pop, rotateDirection, controller);
}
void Perform3DRotate(RotationType rotationType, RotationDirection rotateDirection, UIViewController v)
{
var layer = RotationLayer();
var cube = CubeTransform(rotateDirection, layer);
switch (rotationType)
{
case RotationType.Pop:
this.PopViewController(animated: false);
break;
case RotationType.Push:
this.PushViewController(this, animated: false); //todo: make sure the SWIFT line below for controller! means THIS
// self.pushViewController(controller!, animated: false)
break;
default:
break;
}
layer.AddSublayer(LayerFromView(this.View, cube));
this.View.AddSubview(BackgroundView(UIColor.White));
this.View.Layer.AddSublayer(layer);
layer.AddAnimation(RotationAnimation(rotateDirection), forkey: "rotate");//FIXME parameter
}
CATransform3D CubeTransform(RotationDirection rotateDirection, CALayer layer)
{
var cube = CATransform3D.MakeTranslation(0, 0, 0);//FIXME and all below
layer.AddSublayer(LayerFromView(this.View, transform: cube);
cube = CATransform3D.Rotate(cube, CGFloat(radians(90)), 0, 1, 0);
cube = CATransform3D.Translate(cube,CubeSize(), 0, 0);
if (rotateDirection == RotationDirection.Left)
{
cube = CATransform3D.Rotate(cube, CGFloat(radians(90)), 0, 1, 0);
cube = CATransform3D.Translate(cube, CubeSize(), 0, 0);
cube = CATransform3D.Rotate(cube, CGFloat(radians(90)), 0, 1, 0);
cube = CATransform3D.Translate(cube, CubeSize(), 0, 0);
}
return cube;
}
FCRotateLayer RotationLayer()
{
var layer = new FCRotateLayer();
layer.Frame = this.View.Frame;
layer.AnchorPoint = new CoreGraphics.CGPoint(x: 0.5, y: 0.5);
CATransform3D transform = CATransform3D.Identity;
transform.m34 = 1 / -750;
layer.SublayerTransform = transform;
return layer;
}
CAAnimation RotationAnimation(RotationDirection direction)
{
CATransaction.Flush();
var animationGroup = new CAAnimationGroup();
animationGroup.Duration = 0.4;
var rotation = new CABasicAnimation();
var translationX = new CABasicAnimation();
if (direction == RotationDirection.Right)
{
rotation = new CABasicAnimation() { KeyPath = "sublayerTransform.rotation.y" };
//rotation.To = NSNumber(float: radians(-90))//FIXME
translationX = new CABasicAnimation() { KeyPath = "sublayerTransform.rotation.x" };
translationX.To = NSNumber(float: Float(TranslationForAnimation()));//FIXME
}
else if (direction == RotationDirection.Left)
{
rotation = new CABasicAnimation() { KeyPath = "sublayerTransform.rotation.y" };
//rotation.To = NSNumber(float: radians(90))//FIXME
translationX = new CABasicAnimation() { KeyPath = "sublayerTransform.rotation.x" };
translationX.To = NSNumber(float: Float(TranslationForAnimation()));//FIXME
}
var translationZ = new CABasicAnimation() { KeyPath = "sublayerTransform.translation.z" };
translationZ.To = NSNumber(float: Float(TranslationForAnimation()));//FIXME
animationGroup.Animations = new CAAnimation[3] { rotation, translationX, translationZ };
animationGroup.FillMode = kCAFillModeForwards; /// //FIXME TBD what is the constant? where is it?
animationGroup.RemovedOnCompletion = false;
animationGroup.Delegate = this; // or should this be = to aiimationGroup?
animationGroup.TimingFunction = CAMediaTimingFunction.FromName("kCAMediaTimingFunctionEaseInEaseOut");
return animationGroup;
}
CALayer LayerFromView(UIView view)
{
var rect = new CGRect(0, 0, this.View.Bounds.Size.Width, this.View.Bounds.Size.Height);
var imageLayer = new CALayer();
imageLayer.AnchorPoint = new CGPoint(1, 1);
imageLayer.Frame = rect;
UIGraphics.BeginImageContextWithOptions(view.Frame.Size, false, UIScreen.MainScreen.Scale);
// No idea how to convert "if..let..??" from swift.
//
//if let context = UIGraphicsGetCurrentContext() {//FIXME
//view.layer.renderInContext(context)
//}
UIImage image = new UIImage( = UIGraphics.GetImageFromCurrentImageContext();//FIXME
UIGraphics.EndImageContext();
imageLayer.Contents = image.CGImage;
return imageLayer;
}
CALayer LayerFromView(UIView view, CATransform3D transform)
{
var layer = LayerFromView(view);
layer.Transform = transform;
return layer;
}
System.nfloat Radians(System.nfloat degrees)
{
return degrees * float(M_PI) / 180; //FIXME
}
System.nfloat TranslationForAnimation()
{
return CubeSize() / 2;
}
System.nfloat CubeSize()
{
return UIScreen.MainScreen.Bounds.Width;
}
FCBlackBackgroundView BackgroundView(UIColor color)
{
var view = new FCBlackBackgroundView() { Frame = this.View.Frame };
view.BackgroundColor = color;
return view;
}
}
}
/*
public override func animationDidStop(anim: CAAnimation, finished flag: Bool)//FIXME no suitable overrride found
{
if let layers = self.view.layer.sublayers {
for layer in layers {
if layer.isKindOfClass(FCRotateLayer.classForCoder()) {
layer.removeFromSuperlayer()
}
}
}
for view in self.view.subviews {
if view.isKindOfClass(FCBlackBackgroundView.classForCoder()) {
view.removeFromSuperview()
}
}
}
*
*/