Why am I unable to add anchor to arView scene after using removeAll()? - swift

I'm trying to add and remove an anchor to my scene, but after removing it I'm unable to add it again. This might be because of the anchors added to the scene in the session function, but I'm not sure.
Do I need to run the session function again to add the anchorEntity to the scene again(not managed to do it due to some errors), or is there something else I'm missing...
Here is my code:
import UIKit
import RealityKit
import ARKit
class fvBoat: UIViewController, ARSessionDelegate {
#IBOutlet var arView: ARView!
let fvBoatAnchor = try! Vessel.loadFvBoatScene()
var imageAnchorToEntity: [ARImageAnchor: AnchorEntity] = [:]
override func viewDidLoad() {
super.viewDidLoad()
let fvBoat = fvBoatAnchor.fvBoatObject as? Entity & HasCollision
arView.installGestures(for: fvBoat!)
fvBoatAnchor.generateCollisionShapes(recursive: true)
// arView.scene.addAnchor(fvBoatAnchor)
arView.session.delegate = self
}
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
anchors.compactMap { $0 as? ARImageAnchor }.forEach {
let anchorEntity = AnchorEntity()
let modelEntity = fvBoatAnchor.fvBoatObject!
anchorEntity.addChild(modelEntity)
arView.scene.addAnchor(anchorEntity)
anchorEntity.transform.matrix = $0.transform
imageAnchorToEntity[$0] = anchorEntity
}
}
func installGestures(on object:ModelEntity){
object.generateCollisionShapes(recursive: true)
arView.installGestures(.all, for: object)
}
func leaveScene() {
arView?.session.pause()
arView?.session.delegate = nil
arView?.scene.anchors.removeAll()
arView?.removeFromSuperview()
arView?.window?.resignKey()
arView = nil
}
#IBAction func leaveScene(_ sender: Any) {
leaveScene()
}
#IBAction func addAnchor(_ sender: Any) {
arView.scene.addAnchor(fvBoatAnchor)
}
#IBAction func clearScene(_ sender: Any) {
arView.scene.anchors.removeAll()
}
}

Related

How can I replace a ModelEntity in RealityKit?

I want to switch the ModelEntity from the fvBoatAnchor.fvBoatObject to the fvBridgeAnchor.fvBridgeObject with the click of a button, but I'm not sure how I do it, while also keeping the gestures and collisions as well as the image tracker after changing the entity.
Here is my code
import UIKit
import RealityKit
import ARKit
class fvVessel: UIViewController, ARSessionDelegate {
#IBOutlet var arView: ARView!
#IBOutlet weak var imageView: UIImageView!
let fvBoatAnchor = try! FV.loadFvBoatScene()
let fvBridgeAnchor = try! FV.loadFvBridgeScene()
var imageAnchorToEntity: [ARImageAnchor: AnchorEntity] = [:]
override func viewDidLoad() {
super.viewDidLoad()
let fvBoat = fvBoatAnchor.fvBoatObject as? Entity & HasCollision
arView.installGestures(for: fvBoat!)
fvBoatAnchor.generateCollisionShapes(recursive: true)
arView.scene.addAnchor(fvBoatAnchor)
arView.session.delegate = self
}
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
anchors.compactMap { $0 as? ARImageAnchor }.forEach {
let anchorEntity = AnchorEntity()
let modelEntity = fvBoatAnchor.fvBoatObject!
anchorEntity.addChild(modelEntity)
arView.scene.addAnchor(anchorEntity)
anchorEntity.transform.matrix = $0.transform
imageAnchorToEntity[$0] = anchorEntity
imageView.isHidden = true
}
}
// func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
// anchors.compactMap { $0 as? ARImageAnchor }.forEach {
// let anchorEntity = imageAnchorToEntity[$0]
// anchorEntity?.transform.matrix = $0.transform
// }
// }
func installGestures(on object:ModelEntity){
object.generateCollisionShapes(recursive: true)
arView.installGestures(.all, for: object)
}
func leaveScene() {
arView?.session.pause()
arView?.session.delegate = nil
arView?.scene.anchors.removeAll()
arView?.removeFromSuperview()
arView?.window?.resignKey()
arView = nil
}
#IBAction func leaveScene(_ sender: UIButton) {
leaveScene()
}
}

Receiving Xcode notification on Reality Composer animation end

I have the following Reality Composer project that loads properly. As you can see, when the animation completes, it should notify with the keyword "attackComplete".
How do I get this notification?
import RealityKit
import ARKit
class ViewController: UIViewController, ARSessionDelegate {
#IBOutlet var arView: ARView!
override func viewDidLoad() {
super.viewDidLoad()
let boxAnchor = try! Experience.loadOrcAttack()
arView.session.delegate = self
arView.scene.anchors.append(boxAnchor)
print("done")
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
print(anchors)
}
}
With Reality Composer's notifications you can implement two scenarios:
Action listener
This is your case and it's easy to implement using
public var onAction: ((RealityKit.Entity?) -> Swift.Void)?.
import UIKit
import RealityKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
let scene = try! Experience.loadScene()
override func viewDidLoad() {
super.viewDidLoad()
arView.scene.anchors.append(scene)
scene.actions.attackCompleted.onAction = notificationID // listener
}
fileprivate func notificationID(_ entity: Entity?) {
print(scene.actions.attackCompleted.identifier)
}
}
Here is one more example of how .onAction completion handler can be used.
Trigger for action
When you need to notify Reality Composer's scene to play an action use the following scenario:
import UIKit
import RealityKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
let scene = try! Experience.loadScene()
override func viewDidLoad() {
super.viewDidLoad()
arView.scene.anchors.append(scene)
}
#IBAction func press(_ sender: UIButton) {
scene.notifications.spinner.post() // trigger for action
}
}
or use a subscript for [NAME.NotificationTrigger]:
#IBAction func press(_ sender: NSButton) {
scene.notifications.allNotifications[0].post()
}
Here's one more example of how .post() instance method can be used.
P. S.
If you need more info, read this post.

Subclassed NSView to notify ViewController of action

I have subclassed NSView to receive a dropped folder in order to get its URL.
I've been getting the URL to my ViewController class by accessing a property set in my custom NSView class.
import Cocoa
class DropView: NSView {
var droppedURL : URL!
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
public required init?(coder: NSCoder) {
super .init(coder: coder)
registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL])
}
public override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
return NSDragOperation.copy
}
public override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
NSDragOperation.copy
}
public override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pboard = sender.draggingPasteboard
let urlString = pboard.string(forType: NSPasteboard.PasteboardType.fileURL)
let folderURL = URL(string: urlString!)
print(folderURL)
droppedURL = folderURL
return true
}
}
How can I let my ViewController know when a folder has been dropped onto my NSView and that a URL has been successfully captured? Is there a way other than posting a notification?
Usually you'd use delegates or closures for this. I prefer closures because they're so clean, but it's up to you.
First, define your closure in DropView:
class DropView: NSView {
var droppedURL : URL!
var droppedSuccessfully: ((URL) -> Void)? /// here!
Then, call it just like how you'd call a function. Make sure to pass in your folderURL too.
public override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pboard = sender.draggingPasteboard
let urlString = pboard.string(forType: NSPasteboard.PasteboardType.fileURL)
let folderURL = URL(string: urlString!)
print(folderURL)
droppedURL = folderURL
droppedSuccessfully?(folderURL) /// here!
return true
}
Finally, assign the closure back in your ViewController.
override func viewDidLoad() {
super.viewDidLoad()
...
/// prevent retain cycle
yourDropView.droppedSuccessfully = { [weak self] url in
print("URL received: \(url)")
}
}

Could not cast value of type 'Pong.MenuVC' (0x1072ea808) to 'Pong.GameViewController'

I have built an app using Xcode and swift 5.
Every time I click the "Easy", "Medium", "Hard" or "2 Player" button I get an error:
Could not cast value of type 'Pong.MenuVC' (0x1072ea808) to
'Pong.GameViewController'
Does anyone know how to fix it?
Thank you for your help.
Code of MenuVC:
enum gameType {
case easy
case medium
case hard
case player2
}
class MenuVC : UIViewController {
#IBAction func Player2(_ sender: Any) {
moveToGame(game: .player2)
}
#IBAction func Easy(_ sender: Any) {
moveToGame(game: .easy)
}
#IBAction func Medium(_ sender: Any) {
moveToGame(game: .medium)
}
#IBAction func Hard(_ sender: Any) {
moveToGame(game: .hard)
}
```
**Code with Thread 1: signal SIGABRT:**
```swift
func moveToGame(game : gameType) {
let gameVC = self.storyboard?.instantiateViewController(withIdentifier: "gameVC") as! GameViewController
currentGameType = game
self.navigationController?.pushViewController(gameVC, animated: true)
}
}
Code of GameViewController:
import UIKit
import SpriteKit
import GameplayKit
var currentGameType = gameType.medium
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.size = view.bounds.size
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
In your storyboard the id gameVC is of type MenuVC not GameViewController so change class name of the vc
let gameVC = self.storyboard?.instantiateViewController(withIdentifier: "gameVC") as! GameViewController

Swift AVFoundation

This is actually two problems
they are marked with //*****************************
I put the entire class in this post,
the first is
//====================================
//====================================
#IBAction func btnMakeReport(_ sender: Any) {
let settings = AVCapturePhotoSettings()
//**************** self is erroring... getting...
// /Users/vyoumans/Documents/vyDEVELOPMENT/iOS/TESTS/camtest11/camtest11/VCCam02a.swift:104:61: Argument type 'VCCam02a' does not conform to expected type 'AVCapturePhotoCaptureDelegate'
photoOutput?.capturePhoto(with: settings, delegate: self)
Could I get some sugestions on why self would not confirm to AVCapturePhotoCaptureDelagate.
the second...
//************** image is error... but I define image above.
I declare image at the begining of the class. This error in //====================================
extension ViewController: AVCapturePhotoCaptureDelegate {
at bottom of app
Thanks
//-------- begining of code . ------------
'
import UIKit
import AVFoundation
//====================================
class VCCam02a: UIViewController {
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamrera: AVCaptureDevice?
var photoOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
// var image: UIImage?
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
setupCaptureSession()
setupDevice()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
//====================================
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//====================================
//====================================
func setupCaptureSession() {
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
//====================================
func setupDevice() {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let devices = deviceDiscoverySession.devices
for device in devices {
if device.position == AVCaptureDevice.Position.back {
backCamera = device
} else if device.position == AVCaptureDevice.Position.front {
frontCamera = device
}
}
currentCamrera = backCamera
}
//====================================
func setupInputOutput() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamrera!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
} catch {
print(error)
}
}
//====================================
func setupPreviewLayer() {
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer?.frame = self.view.frame
self.view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
//====================================
func startRunningCaptureSession() {
captureSession.startRunning()
}
//====================================
//====================================
#IBAction func btnMakeReport(_ sender: Any) {
let settings = AVCapturePhotoSettings()
//**************** self is erroring... getting...
// /Users/vyoumans/Documents/vyDEVELOPMENT/iOS/TESTS/camtest11/camtest11/VCCam02a.swift:104:61: Argument type 'VCCam02a' does not conform to expected type 'AVCapturePhotoCaptureDelegate'
photoOutput?.capturePhoto(with: settings, delegate: self)
// performSegue(withIdentifier: "showPhoto_Segue", sender: nil)
}
//====================================
#IBAction func btnCancel(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
//====================================
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPhoto_Segue" {
let VCPreviewViewController = segue.destination as! VCPreviewViewController
VCPreviewViewController.image = self.image
}
}
//====================================
//====================================
//====================================
//====================================
//====================================
//====================================
//====================================
//====================================
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
//====================================
extension ViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation() {
//************** image is error... but I define image above.
///Users/vyoumans/Documents/vyDEVELOPMENT/iOS/TESTS/camtest11/camtest11/VCCam02a.swift:143:13: Use of unresolved identifier 'image'
image = UIImage(data: imageData)
performSegue(withIdentifier: "showPhoto_Segue", sender: nil)
}
}
}
//====================================
//====================================
//====================================
//====================================
//====================================
'
Ok, this line:
photoOutput?.capturePhoto(with: settings, delegate: self)
Seems to be the problem. It sounds like you're getting a compiler error that self does not conform to the AVCapturePhotoCaptureDelegate protocol.
That's pretty self-explanatory. If your class conforms to the AVCapturePhotoCaptureDelegate protocol, you need to add that conformance to the class definition, or to an extension:
class VCCam02a: UIViewController, AVCapturePhotoCaptureDelegate
You will then need to implement all the methods in that protocol needed to handle the type of capture you are doing. To Quote the docs:
All methods in this protocol are optional at compile time, but at run
time your delegate object must respond to certain methods depending on
your photo settings:
(See the Apple docs on the protocol for more information.)