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

1,354 阅读4分钟

SCNNode 贴图类型

在 AR 开发中,我们经常会给物体贴图,比如:

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 的总内存却不断上涨。

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

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.

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

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

UIImage

首先是UIImage,贴图后效果如下

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

动画支持

UIImage本身无法进行动画,不过contentsTransform属性是Animatable的,可能用来做变形动画。

UIImageView

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

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

动画支持

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

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

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 也是不推荐的,可能有各种 bug 行为。

也就是说,外层 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 游戏开发框架,可以开发出各种你想要的动画效果。但是这个框架很少有人用,实际开发中也发现,性能疑似存在问题。