diff --git a/Snap/Core/Infrastructure/Extension/UIView/UIView+Render.swift b/Snap/Core/Infrastructure/Extension/UIView/UIView+Render.swift index fcf33f0..d0baa54 100644 --- a/Snap/Core/Infrastructure/Extension/UIView/UIView+Render.swift +++ b/Snap/Core/Infrastructure/Extension/UIView/UIView+Render.swift @@ -1,8 +1,38 @@ import UIKit +import SpriteKit +import SceneKit + +/** + Adds a static image view on top of all SCNViews, GLKViews and SKViews for the given view hierarchy so these can be captured. Returns the created views. + */ +func addImagesForRenderedViews(_ view: UIView) -> [UIView] { + + func add(_ image: UIImage) -> [UIView] { + let imageView = UIImageView(image: image) + imageView.frame = view.bounds + view.addSubview(imageView) + return [imageView] + } + + if let scnview = view as? SCNView { + return add(scnview.snapshot()) + } else if let glview = view as? GLKView { + return add(glview.snapshot) + } else if let skview = view as? SKView { + return add(UIImage(cgImage: skview.texture(from: skview.scene!)!.cgImage())) + } else { + return view.subviews.flatMap(addImagesForRenderedViews) + } +} extension UIView { /// Extract image data with the whole hierarchy from `UIView` func render() -> UIImage? { + + // SpriteKit refuses to draw its hierarchy: render an image of the spritekit view and add it to the view to be able to capture it + let addedViews = addImagesForRenderedViews(self) + defer { addedViews.forEach { $0.removeFromSuperview() } } + if #available(iOS 10.0, *) { let imageRendererFormat = UIGraphicsImageRendererFormat.default() imageRendererFormat.opaque = true @@ -10,7 +40,6 @@ extension UIView { bounds: bounds ) return renderer.image { context in - drawHierarchy(in: bounds, afterScreenUpdates: true) layer.render(in: context.cgContext) } } @@ -21,7 +50,6 @@ extension UIView { UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0) let context = UIGraphicsGetCurrentContext()! - drawHierarchy(in: bounds, afterScreenUpdates: true) layer.render(in: context) return UIGraphicsGetImageFromCurrentImageContext() } diff --git a/SnapTests/Snap/SnapTests/reference_test_scenekit_view@2x.png b/SnapTests/Snap/SnapTests/reference_test_scenekit_view@2x.png new file mode 100644 index 0000000..c466a20 Binary files /dev/null and b/SnapTests/Snap/SnapTests/reference_test_scenekit_view@2x.png differ diff --git a/SnapTests/Snap/SnapTests/reference_test_spritekit_view@2x.png b/SnapTests/Snap/SnapTests/reference_test_spritekit_view@2x.png new file mode 100644 index 0000000..8a623d3 Binary files /dev/null and b/SnapTests/Snap/SnapTests/reference_test_spritekit_view@2x.png differ diff --git a/SnapTests/SnapTests.swift b/SnapTests/SnapTests.swift index aec8aa6..7c8e758 100644 --- a/SnapTests/SnapTests.swift +++ b/SnapTests/SnapTests.swift @@ -1,5 +1,7 @@ import XCTest import UIKit +import SpriteKit +import SceneKit private final class SnapTests: XCTestCase { @@ -34,8 +36,8 @@ private final class SnapTests: XCTestCase { UIColor.white.cgColor ] layer.locations = [0.0, 1.0] - layer.startPoint = CGPoint(x: 0.0, y: 1.0) - layer.endPoint = CGPoint(x: 0.0, y: 1.0) + layer.startPoint = CGPoint(x: 0.0, y: 0.0) + layer.endPoint = CGPoint(x: 1.0, y: 0.0) expect(layer).toMatchSnapshot() } @@ -67,4 +69,51 @@ private final class SnapTests: XCTestCase { expect(view).toMatchSnapshot(for: .iPadDevices) } + + func test_spritekit_view() { + let scene = SKScene(size: CGSize(width: 100, height: 100)) + scene.backgroundColor = .yellow + + let rect = SKShapeNode(rectOf: CGSize(width: 10, height: 10)) + rect.fillColor = .red + rect.strokeColor = .clear + rect.position = CGPoint(x: 50, y: 50) + scene.addChild(rect) + + let view = SKView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + view.presentScene(scene) + + expect(view).toMatchSnapshot() + } + + func test_scenekit_view() { + let scene = SCNScene() + + // light + let lightNode = SCNNode() + lightNode.light = SCNLight() + lightNode.light!.type = .omni + lightNode.position = SCNVector3(x: 0, y: 10, z: 10) + scene.rootNode.addChildNode(lightNode) + + // cube + let cubeGeometry = SCNBox(width: 5, height: 5, length: 5, chamferRadius: 0) + cubeGeometry.firstMaterial!.diffuse.contents = UIColor.red + let cubeNode = SCNNode(geometry: cubeGeometry) + scene.rootNode.addChildNode(cubeNode) + + // camera + let cameraNode = SCNNode() + cameraNode.camera = SCNCamera() + scene.rootNode.addChildNode(cameraNode) + cameraNode.position = SCNVector3(x: 10, y: 10, z: 10) + cameraNode.constraints = [SCNLookAtConstraint(target: cubeNode)] + + let view = SCNView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + view.backgroundColor = .yellow + view.scene = scene + + expect(view).toMatchSnapshot() + } + }