SCNNode到底应该怎么贴图?UIImage,UIImageView,CALayer用哪一个?

SCNNode 贴图类型

在 AR 开发中,咱们常常会给物体贴图,好比:swift

planeNode.geometry?.firstMaterial?.diffuse.contents = image
cubeNode.geometry?.firstMaterial?.diffuse.contents = image
复制代码

这样子,可是 SCNNode 类型支持的贴图有不少种,官方文档中说明中主要有:UIColor,UIImage, CALayer,NSURL,SKScene,SKTexture...甚至还支持AVCaptureDevice相机和AVPlayer播放视频。数组

实际开发中,我还发现 UIView 也是支持的,size 须要取整,否则可能崩溃。另外我还发现 UIView 做为贴图,疑似存在内存泄露的问题:View 和 Node 都销毁了,但 app 的总内存却不断上涨。bash

苹果官方目前(至 iOS13)都并不推荐贴图使用 UIView 类,可能会有潜在的布局问题,内存问题等。苹果论坛上官方员工的回复forums.developer.apple.com/message/340… session

This can be a color (NSColor, UIColor, CGColorRef), 
an image (NSImage, UIImage, CGImageRef),
a layer (CALayer), a path (NSString or NSURL), a SpriteKit scene (SKScene),
a texture (SKTexture, id<MTLTexture> or GLKTextureInfo), 
or a floating value between 0 and 1 (NSNumber) for metalness and roughness properties. 
AVCaptureDevice is supported on iOS 11 and AVPlayer is supported on macOS 10.13, iOS 11 and tvOS 11.
复制代码

那么咱们在实际开发中应该用哪一个?它们有什么区别呢?app

下面咱们主要来尝试理解一下UIImage,UIImageView,UIScrollView 及 CALayer 这三种类型的区别。框架

UIImage

首先是UIImage,贴图后效果以下 布局

能够看到,图片做为贴图时,能够真实还原图片中每一个像素的颜色,当形状不一致时,默认会被拉伸铺满。

动画支持

UIImage自己没法进行动画,不过contentsTransform属性是Animatable的,惋惜我测试没反应,不知道是否是 UIImage 类型压根就不支持动画。。。测试

UIImageView

UIImageView 的贴图方式是根据 Frame 的值进行的。好比 x 与 y 的偏移量会影响贴图的原点。而 size 则决定了清晰度,当 size 为width: 30, height: 30 时,而立方体宽高 1 米时效果以下。 动画

当拉伸时,也会被拉伸平铺,当 x 或 y 方向有偏移时,则会一边空出一段距离,而另外一侧被截断。好比长方体中上面空了,下面被截断了: spa

动画支持

UIImageView 做为 UIView 的子类,在平时开发中是支持 UIView 动画和 CAAnimation 动画。可是在 SCNNode 贴图中,这两种动画都无效。

能够用定时器去改变 UIImageView 的 frame 值来改变贴图大小和位置,可是没有过渡动画。

contentsTransform属性测试仍是没反应,谁知道这个怎么用。。。

UIScrollView

UIScrollView 本质上和 UIImageView 是同样的,可是平时开发中除了UIView 动画和 CAAnimation 动画,它还有 Scroll 的动画。

这个支持怎么样?咱们来测试一下

看来支持的效果很好。终于能在 SCNNode 的贴图上用上动画效果了!

CALayer

因为 CALayer 通常是从 UIView 中抽出来的,咱们就直接用前面 UIImageView 和 UIScrollView 的 layer 进行测试。能够看到基本表现和 UIImageView 是同样的,只是清晰度低了不少,缘由不明:

可是,当 frame 的 x、y 有偏移时,发生偏移与截断的形式却并不同。下面是贴图时 frame = CGRect(x: 0, y: 0, width: 30, height: 30),延时 5 秒后frame = CGRect(x: 10, y: 0, width: 30, height: 20),可见 CALayer 与 UIView 的坐标系仍是不一样。

动画支持

在平时开发中,CALayer 是重要的动画层,能够完成不少动画效果,可是在贴图时,发现全部 CALayer 动画都没法正常显示。那么 UIScrollView 的 layer 呢?它是否还能正常显示 scroll 的动画效果?

下面为了更好区分滚动先后的图片,我将第二张图片设置为UIImage(),显示为黑色。

能够看出来,CALyer 只能显示 UIScrollView 滚动前和滚动后的图像,不显示中间的滚动过程动画。

苹果官方对贴图动画的说明 官方说明了动画的三种途径:视频,CALayer 的 sublayer 动画,SpriteKit

同时说明了,若是是 UIView 的 layer 也是能够的,可是不能已经在其余地方显示过。

也就是说,外层 CALayer 的动画不支持,可是手动建立 sublayer 并添加动画是能够的。

总结

类型 清晰度控制 偏移调整 UIView 动画 CAAnimation Scroll 效果
UIImage 图片自己清晰度,没法调整 没法偏移调整 -- -- --
UIImageView frame.size 的清晰度,可调整 可调整偏移 不支持 不支持 --
UIScrollView frame.size 的清晰度,可调整 可调整偏移 不支持 不支持 支持
CALayer 比frame.size 的清晰度更低 可调整偏移,效果不一样 -- sublayer支持 不支持

因此,若是想在 SCNNode 的贴图上显示平面动画,可考虑用:

  • gif 方式或 image 数组
  • UIScrollView 滚动方式(官方不推荐)
  • 逐帧调整 UIView 或子类的 frame/color 值(官方不推荐)
  • AVPlayer 播放视频(官方推荐)
  • CALayer 的 sublayer 动画(官方推荐)
  • SKScene 即 SpriteKit 框架的 scene(官方推荐)
    • 苹果在WWDC2017 的 session609 里演示过

其中功能最强大的就是 SpriteKit 框架了,它是苹果的 2D 游戏开发框架,能够开发出各类你想要的动画效果。可是这个框架不多有人用吧