【iOS】用Layer创建一个三维模型以及拖动
关于CALayer的介绍以及基本属性,在这篇博客中有交代:CoreAnimation —— CALayer
这篇博客讲述简单的通过对layer的transform属性的设置一个CATransform3D来进行自定义三维图形,甚至后续的处理。
通常简单的仿射变换我们也是通过对其的transform属性进行设置。不过这里设置的是一个3D变换类。如果线性代数很好的话,那应该能够理解内部具体做了如何的矩阵运算。
首先我子类化一个UIView对象,把图形的绘制在这个自定义View上进行。
接口方面
@property (nonatomic, assign, readonly) CGFloat side;
@property (nonatomic, assign, readonly) BOOL autoAnimate; /**
* 创建一个正方体对象
*
* @param frame 位置
* @param side 边长
* @param animate 创建后是否自动旋转展示
*
* @return 正方体对象视图
*/
+ (HRCube *)cube3DWithFrame:(CGRect)frame side:(CGFloat)side autoAnimate:(BOOL)animate;
工厂方法的实现就是快速实例化并设置属性
+ (HRCube *)cube3DWithFrame:(CGRect)frame side:(CGFloat)side autoAnimate:(BOOL)animate
{
HRCube *cube3D = [[HRCube alloc] initWithFrame:frame];
cube3D.side = side;
cube3D.autoAnimate = animate; return cube3D;
}
类别中有两个内部成员
CALayer *_cubeLayer; //main layer
GLKMatrix4 _rotMatrix;
第一个是我们需要绘制正方体的layer。
第二个是在导入GLKit框架后,从中引入的一个矩阵变换类,我们在触摸屏幕拖动时,需要使用到这个类对正方体进行旋转处理。
初始化主layer
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_cubeLayer = [CALayer layer];
_cubeLayer.frame = self.bounds;
_cubeLayer.contentsScale = [UIScreen mainScreen].scale;
}
return self;
}
正式绘制正方体
- (void)setSide:(CGFloat)side
{
_side = side; // NSLog(@"%@", NSStringFromCGRect(_cubeLayer.bounds)); //正
[self addCubeLayer:@[@0, @0, @(_side/2), @0, @0, @0, @0]];
//背
[self addCubeLayer:@[@0, @0, @(-_side/2), @(M_PI), @0, @0, @0]];
//左
[self addCubeLayer:@[@(-_side/2), @0, @0, @(-M_PI_2), @0, @1, @0]];
//右
[self addCubeLayer:@[@(_side/2), @0, @0, @(M_PI_2), @0, @1, @0]];
//上
[self addCubeLayer:@[@0, @(-_side/2), @0, @(-M_PI_2), @1, @0, @0]];
//下
[self addCubeLayer:@[@0, @(_side/2), @0, @(M_PI_2), @1, @0, @0]]; CATransform3D transform3D = CATransform3DIdentity;
transform3D.m34 = -1.0/2000;
_cubeLayer.sublayerTransform = transform3D; [self.layer addSublayer:_cubeLayer];
}
这里的m34不是星体- -,3D变形矩阵的m34通常应该设置为-1/EYE_DISTANCE,这里设置为2000已经足够好。
addCubeLayer的实现
//添加sublayers
- (void)addCubeLayer:(NSArray *)params
{
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.contentsScale = [UIScreen mainScreen].scale;
gradient.bounds = CGRectMake(0, 0, _side, _side);
gradient.position = self.center;
gradient.colors = @[(id)kGrayColor, (id)kBlackColor];
gradient.locations = @[@0, @1];
gradient.startPoint = CGPointMake(0, 0);
gradient.endPoint = CGPointMake(0, 1);
//抗锯齿
gradient.shouldRasterize = YES; CATransform3D trans3D = CATransform3DMakeTranslation([params[0] floatValue], [params[1] floatValue], [params[2] floatValue]);
CATransform3D rotate3D = CATransform3DRotate(trans3D , [params[3] floatValue], [params[4] floatValue], [params[5] floatValue], [params[6] floatValue]);
CATransform3D transform3D = rotate3D;
// CATransform3D transform3D = CATransform3DRotate(trans3D, [params[3] floatValue], [params[4] floatValue], [params[5] floatValue], [params[6] floatValue]); gradient.transform = transform3D; [_cubeLayer addSublayer:gradient];
}
这里为了更方便观察,使用的是渐变Layer,颜色为宏,可以自由修改。
注意给其抗锯齿设为了YES。通常我们还可以在整个项目的info.plist文件中设置该内容,不过设置会对整个项目的性能产生不好的影响,所以一般来说,需要的时候再设置也是一个不错的选择。
这样我们就简单的创建好了一个正方体,不过当前摆放很正,所以只能看到一个正方形平面,下面我们给他设置自动旋转后给他一个沿着坐标轴的角度偏转,再无限围着y轴转就OK了。
//添加自定义旋转展示动画
- (void)addAnimation
{
_cubeLayer.sublayerTransform = CATransform3DRotate(_cubeLayer.sublayerTransform, M_PI/9.0, 0.5, 0.5, 0.5); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.rotation.y"];
animation.toValue = @(MAXFLOAT);
animation.duration = MAXFLOAT;
[_cubeLayer addAnimation:animation forKey:@"rotation"];
}
这时运行程序我们就能看到一个旋转的三维正方体了
下面给其扩展一个拖动收拾旋转功能
首先给view增加一个拖动收拾,然后在Action中实现
#pragma mark - PanGesture
- (void)panRotate:(UIPanGestureRecognizer *)ges
{
static CGPoint start;
if (ges.state == UIGestureRecognizerStateBegan) {
start = [ges locationInView:self];
} else if (ges.state == UIGestureRecognizerStateChanged) {
CATransform3D transform = _cubeLayer.sublayerTransform;
_rotMatrix = GLKMatrix4MakeWithArray((void *)&transform); CGPoint loc = [ges locationInView:self];
CGPoint diff = CGPointMake(start.x-loc.x, start.y-loc.y); float rotX = 1 * GLKMathDegreesToRadians(diff.y/2.0);
float rotY = -1 * GLKMathDegreesToRadians(diff.x/2.0); bool isInvertible;
GLKVector3 xAxis = GLKMatrix4MultiplyVector3(GLKMatrix4Invert(_rotMatrix, &isInvertible),
GLKVector3Make(1, 0, 0));
_rotMatrix = GLKMatrix4Rotate(_rotMatrix, rotX, xAxis.x, xAxis.y, xAxis.z);
GLKVector3 yAxis = GLKMatrix4MultiplyVector3(GLKMatrix4Invert(_rotMatrix, &isInvertible),
GLKVector3Make(0, 1, 0));
_rotMatrix = GLKMatrix4Rotate(_rotMatrix, rotY, yAxis.x, yAxis.y, yAxis.z); _cubeLayer.sublayerTransform = *((CATransform3D *)&_rotMatrix); start = loc;
}
}
经过对矩阵的实时处理后,我们可以使用拖动收拾来在视图中实时的调整3D正方体的旋转了。
Demo源码:
CSDN:点击打开链接
GitHub:Rannie / HRCube3D
喜欢的话可以去点个星:)
以上就是本篇博客全部内容,欢迎指正和交流。转载注明出处~
【iOS】用Layer创建一个三维模型以及拖动的更多相关文章
- iOS中如何创建一个滑出式导航面板(1)
本文将介绍如何创建类似Facebook和Path iOS程序中的滑出式导航面板. 向右滑动 滑出式设计模式可以让开发者在程序中添加常用的导航功能,又不会浪费屏幕上宝贵的空间.用户可以在任意时间滑出导航 ...
- 在iOS 4中创建一个LDGradientView样式的渐变视图
本教程将演示如何在 Swift 4 中创建一个多功能的.@IBDesignable 样式的渐变视图类.你可以将 CAGradientView 放到 storyboard 中,并在设计时预览,或者以编程 ...
- Delphi for iOS开发指南(3):创建一个FireMonkey iOS应用程序
http://cache.baiducontent.com/c?m=9d78d513d9d431a94f9d92697d60c015134381132ba1d0020fa48449e3732b4b50 ...
- IOS开发之小实例--使用UIImagePickerController创建一个简单的相机应用程序
前言:本篇博文是本人阅读国外的IOS Programming Tutorial的一篇入门文章的学习过程总结,难度不大,因为是入门.主要是入门UIImagePickerController这个控制器,那 ...
- 如何用 React Native 创建一个iOS APP?(三)
前两部分,<如何用 React Native 创建一个iOS APP?>,<如何用 React Native 创建一个iOS APP (二)?>中,我们分别讲了用 React ...
- 如何用 React Native 创建一个iOS APP?(二)
我们书接上文<如何用 React Native 创建一个iOS APP?>,继续来讲如何用 React Native 创建一个iOS APP.接下来,我们会涉及到很多控件. 1 AppRe ...
- 如何用 React Native 创建一个iOS APP?
诚然,React Native 结合了 Web 应用和 Native 应用的优势,可以使用 JavaScript 来开发 iOS 和 Android 原生应用.在 JavaScript 中用 Reac ...
- 【iOS开展-50】使用它来创建一个新的类的实现代码包,因此,不自觉地练习简单MVC实验,附带动画
接下来说说代码封装最后一个个案. 最后一种情况看:[iOS开展-48]九宫格案例:自己主动布局.字典转模型运用.id和instancetype差别.xib反复视图运用及与nib关系 (1)代码封装的原 ...
- 【转】ios tableView那些事(一)创建一个简单的tableView
工作也有半年多了!几乎每个项目中的会用到tableview这个神奇而好用的控件,在学习和工作中都会看别人的博客!对我有很大的帮助,就如同站在巨人的肩膀上的感觉吧 哈哈!于是决定重新开始写博客,希望能帮 ...
随机推荐
- Android中GridView的使用——使用自带的SimpleAdapter(简单适配器)
GridView一直是一个系统登录后以九宫格方式展现功能子模块的最佳选择,经过试验和网上资料的查阅,现把实现方式总结一下: 一直是通过自定义Adapter方式,在getView()方法中设置图片的显示 ...
- Objective-c 协议(protocol)
协议的作用类似地C++中对抽象基类的多重继承.类似于Java中的接口(interface)的概念. 协议是多个类共享方法的列表,协议中列出的方法在本类中并没有相应实现,而是别的类来实现这些方法. ...
- WPF与Winform的选择
最近公司计划对ERP系统全面升级,现有的ERP是简单的bs架构系统打算改版成cs.平时如自己写一些工具,小应用都是用winform就足够.但是界面总是很难看,据了解WPF在这一方面会强一些.因为之前对 ...
- c语言: 文件io, 拷贝文件(二进制)
#include <stdio.h> #include <stdlib.h> #define TRAN_SZIE 1024 int copy_bin(char* from, c ...
- projecteuler之58题Spiral primes
package com.android; public class SpiralPrimes { public static void main(String args[]) { long numPr ...
- 深入浅出—JAVA(1)
1.基本概念 JAVA的工作方式 编写源代码文件--用编译器运行源代码(javac)--编译器会产出字节码--通过JAVA虚拟机读取与执行字节码(jvm). JAVA的程序结构 什么是源文件? 源文件 ...
- C陷阱与缺陷(四)
第四章 连接 4.1 什么是连接器 C语言中的一个重要思想就是分别编译,即若干个源程序可以在不同的时候单独进行编译,然后在恰当的时候整合在一起.典型的连接器把由编译器或汇编器生成的若干个目标模块,整合 ...
- centOS下恢复win8引导
正题(非原创): shutdown两次以后确信我的win8引导没有了 百度后找到一个修改grub.cfg文件的方法 这个文件在普通用户下是没有修改的权利的 要在sudo su之后用root权限 vi ...
- [ArcGIS必打补丁]ArcGIS 10.1 SP1 for (Desktop, Engine, Server) Quality Improvement Patch
大家都知道假设希望保证企业级GIS系统的稳定执行,除了使用最新的ArcGIS版本号产品以外,还须要打上相关的补丁. 补丁分为:Service Pack和Patch 比如,假设你使用的ArcGIS10. ...
- Huffman树编码-优先队列实现
Huffman编码是之前一道算法作业题,最近又要复习考试了,先把这个的代码再看一下吧. 算法原理很简单,使用优先队列将两个节点弹出,然后合并节点之后再入队列如此循环做下去即可. 主要问题在于树的修改问 ...