简体   繁体   English

ARKit:如何将 UIView 添加到 ARKit 场景?

[英]ARKit: How can I add a UIView to ARKit Scene?

I am working on an AR project using ARKit.我正在使用 ARKit 进行 AR 项目。

I want to add a UIView to ARKit Scene.我想向 ARKit 场景添加一个 UIView。 When I tap on an object, I want to get information as a "pop-up" next to the object.当我点击一个对象时,我想以该对象旁边的“弹出窗口”形式获取信息。 This information is in a UIView.此信息位于 UIView 中。

Is it possible to add this UIView to ARKit Scene?是否可以将此 UIView 添加到 ARKit 场景中? I set up this UIView as a scene and what can I do then?我将此 UIView 设置为场景,然后我该怎么办? Can I give it a node and then add it to the ARKit Scene?我可以给它一个节点,然后将它添加到 ARKit 场景中吗? If so, how it works?如果是这样,它是如何工作的?

Or is there another way?或者还有其他方法吗?

Thank you!谢谢!

EDIT: Code of my SecondViewController编辑:我的 SecondViewController 的代码

class InformationViewController: UIViewController {

    @IBOutlet weak var secondView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view = secondView
    }
}

EDIT 2: Code in firstViewController编辑 2:firstViewController 中的代码

guard let secondViewController = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else {
    print ("No secondController")
    return
}

let plane = SCNPlane(width: CGFloat(0.1), height: CGFloat(0.1))       
plane.firstMaterial?.diffuse.contents = secondViewController.view

let node = SCNNode(geometry: plane)

I only get a white screen of a plane, not the view.我只看到飞机的白色屏幕,而不是视图。

The simplest (although undocumented) way to achieve that is to set a UIView backed by a view controller as diffuse contents of a material on a SCNPlane (or any other geometry really, but it works best with planes for obvious reasons).实现这一目标的最简单(尽管未记录)方法是将视图控制器支持的UIView设置为SCNPlane上材质的漫反射内容(或任何其他几何体,但由于显而易见的原因,它最适用于平面)。

let plane = SCNPlane()
plane.firstMaterial?.diffuse.contents = someViewController.view
let planeNode = SCNNode(geometry: plane)

You will have to persist the view controller somewhere otherwise it's going to be released and plane will not be visible.您必须将视图控制器保留在某处,否则它将被释放并且平面将不可见。 Using just a UIView without any UIViewController will throw an error.仅使用没有任何UIViewControllerUIView将引发错误。

The best thing about it is that it keeps all of the gestures and practically works just as a simple view.关于它的最好的事情是它保留了所有手势并且实际上就像一个简单的视图一样工作。 For example, if you use UITableViewController 's view you will be able to scroll it right inside a scene.例如,如果您使用UITableViewController的视图,您将能够在场景中直接滚动它。

I haven't tested it on iOS 10 and lower, but it's been working on iOS 11 so far.我还没有在 iOS 10 及更低版本上测试过它,但到目前为止它一直在 iOS 11 上运行。 Works both in plain SceneKit scenes and with ARKit.适用于普通 SceneKit 场景和 ARKit。

I cannot provide you code now but this is how to do it.我现在无法为您提供代码,但这是如何做到的。

  1. Create a SCNPlane .创建一个SCNPlane
  2. Create your UIView with all elements you need.使用您需要的所有元素创建您的UIView
  3. Create image context from UIView .UIView创建图像上下文。
  4. Use this image as material for SCNPlane .将此图像用作SCNPlane材料。

Or even easier make SKScene with label and add it as material for SCNPlane .或者更容易使用标签制作SKScene并将其添加为SCNPlane材料。

To place text in a label in the world you draw it into an image and then attach that image to a SCNNode .要在世界中的标签中放置文本,请将其绘制到图像中,然后将该图像附加到SCNNode

For example:例如:

let text = "Hello, Stack Overflow."
let font = UIFont(name: "Arial", size: CGFloat(size))
let width = 128
let height = 128

let fontAttrs: [NSAttributedStringKey: Any] = 
[NSAttributedStringKey.font: font as UIFont]

let stringSize = self.text.size(withAttributes: fontAttrs)
let rect = CGRect(x: CGFloat((width / 2.0) - (stringSize.width/2.0)),
                  y: CGFloat((height / 2.0) - (stringSize.height/2.0)),
                  width: CGFloat(stringSize.width),
                  height: CGFloat(stringSize.height))

let renderer = UIGraphicsImageRenderer(size: CGSize(width: CGFloat(width), height: CGFloat(height)))
let image = renderer.image { context in

    let color = UIColor.blue.withAlphaComponent(CGFloat(0.5))

    color.setFill()
    context.fill(rect)

    text.draw(with: rect, options: .usesLineFragmentOrigin, attributes: fontAttrs, context: nil)
}

let plane = SCNPlane(width: CGFloat(0.1), height: CGFloat(0.1))
plane.firstMaterial?.diffuse.contents = image

let node = SCNNode(geometry: plane)

EDIT: I added these lines:编辑:我添加了这些行:

let color = UIColor.blue.withAlphaComponent(CGFloat(0.5))

color.setFill()
context.fill(rect)

This lets you set the background color and the opacity.这使您可以设置背景颜色和不透明度。 There are other ways of doing this - which also let you draw complex shapes - but this is the easiest for basic color.还有其他方法可以做到这一点 - 也可以让您绘制复杂的形状 - 但这是基本颜色最简单的方法。

EDIT 2: Added reference to stringSize and rect编辑 2:添加了对stringSizerect引用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM