How to properly end a ARView that uses a Reality Composer project? - swift

My project has a few views before it call a ARview with a reality composer project. I enabled the coaching view in it using the demo code that apple provides. My problem is. If I make a button to go back from that view and call it again, the camera starts to flick from second to second. If I do it again, the flick increases until that, if I do it again, the program crashes. How can I make that view completely new, like the first time I loaded it? I’ve tried removing the anchor from the view.session. Tried pausing the ar session. Could anyone help me with that?
This is my code
import UIKit
import RealityKit
import ARKit
class ARHomeViewController: UIViewController {
#IBOutlet var arView: ARView!
#IBOutlet weak var coachingOverlay: ARCoachingOverlayView!
override func viewDidLoad()
{
super.viewDidLoad()
presentCoachingOverlay()
addScene()
}
/// Begins the coaching process that instructs the user's movement during
/// ARKit's session initialization.
func presentCoachingOverlay() {
coachingOverlay.session = arView.session
coachingOverlay.delegate = self
coachingOverlay.goal = .horizontalPlane
coachingOverlay.activatesAutomatically = false
self.coachingOverlay.setActive(true, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Prevent the screen from being dimmed to avoid interuppting the AR experience.
UIApplication.shared.isIdleTimerDisabled = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
arView?.session.pause()
}
func addScene()
{
guard let anchor = try? ExperienciaCasa.loadMenu() else { return }
anchor.generateCollisionShapes(recursive: true)
arView.scene.addAnchor(anchor)
}
}

Related

How can i create a searchbar like safari's searchbar ? SWIFT

How can i create a searchbar like safari
Just like the photos I posted
That the searchbar becomes small or hidden when the screen is scrolled
...
PHOTOS
It would be great if you could tell, what you've tried before.
That way we can help you better.
In order to do that, you need a view of type UIScrollView (UITableView, UICollectionView, etc.).
Implement this delegate method and it should do the trick.
Adjust the swipeThreshold variable to fit your needs.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let swipeThreshold: CGFloat = 10
let y = scrollView.panGestureRecognizer.translation(in: scrollView).y
if y != 0 && swipeThreshold > y {
navigationController?.setNavigationBarHidden(true, animated: true)
} else {
navigationController?.setNavigationBarHidden(false, animated: true)
}
}
Old answer as reference:
Here is one way to do it — by using SFSafariViewController using SafariServices.
import UIKit
import SafariServices
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// Call this method when you want to show the browser
func doSomething() {
let url = URL(string: "https://www.google.com")!
let safariViewController = SFSafariViewController(url: url)
present(safariViewController, animated: true, completion: nil)
}
}

Is it possible to run 2 independent ARSessions simultaneously?

Since ARKit3 it is possible to run a ARSession() that supports the back and front camera simultaneously.
For example this creates an ARConfiguration for the front camera that supports also Worldtracking.
// session for the front camera
let configuration = ARFaceTrackingConfiguration()
configuration.isWorldTrackingEnabled
sceneView.session.run(configuration)
This example creates a configuration for a back camera session with Face Tracking enabled:
// session for the back camera
let configuration = ARWorldTrackingConfiguration()
configuration.userFaceTrackingEnabled = true
sceneView.session.run(configuration)
I would like to create 2 independent ARConfigurations and ARSessions that run simultaneously. Like this:
So far i tried this code:
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
// back camera view
#IBOutlet var backView: ARSCNView!
// front camera view
#IBOutlet weak var frontView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
frontView.delegate = self
// Create a new scene for back camera
let scene = SCNScene(named: "art.scnassets/ship.scn")!
backView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// session for the back camera
let configuration = ARWorldTrackingConfiguration()
configuration.userFaceTrackingEnabled = true
backView.session.run(configuration)
// session for the front camera
guard ARFaceTrackingConfiguration.isSupported else { return }
let configurationFront = ARFaceTrackingConfiguration()
configurationFront.isLightEstimationEnabled = true
frontView.session.run(configurationFront, options: [])
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
}
The back camera stops its video feed while the front camera is working properly. Any chances to solve this ?
It would also be a solution to run one ARSession and one low level video capture session on the other camera but i am running into the same problems.

ARKit Stereo – Is it possible to run two ARSCNView at the same time?

I was thinking to do some modification to my existing AR app, and I wanted to split the view and add inside 2 ARSCNView in this way users can use the VR Card Box and have a different experience but Xcode is always returning me:
Session (0x102617d10): did fail with error: Error Domain=com.apple.arkit.error Code=102 "Required sensor failed."
So, I'm supposing that I can't run 2 ARSCNView sessions at the same time, or am I wrong?
The answer is: Yes, it's possible.
Use the following code to accomplish it:
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet weak var sceneView: ARSCNView!
#IBOutlet weak var sceneView2: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/ship.scn")!
sceneView.scene = scene
sceneView.isPlaying = true
// Setup for sceneView2
sceneView2.scene = scene
sceneView2.showsStatistics = sceneView.showsStatistics
// Now sceneView2 starts receiving updates
sceneView2.isPlaying = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARWorldTrackingConfiguration()
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
self.updateFrame()
}
}
func updateFrame() {
// Clone pointOfView for Second View
let pointOfView: SCNNode = (sceneView.pointOfView?.clone())!
// Determine Adjusted Position for Right Eye
let orientation: SCNQuaternion = pointOfView.orientation
let orientationQuaternion: GLKQuaternion = GLKQuaternionMake(orientation.x,
orientation.y,
orientation.z,
orientation.w)
let eyePos: GLKVector3 = GLKVector3Make(1.0, 0.0, 0.0)
let rotatedEyePos: GLKVector3 = GLKQuaternionRotateVector3(orientationQuaternion,
eyePos)
let rotatedEyePosSCNV: SCNVector3 = SCNVector3Make(rotatedEyePos.x,
rotatedEyePos.y,
rotatedEyePos.z)
let mag: Float = 0.064 // Interocular distance (in metres)
pointOfView.position.x += rotatedEyePosSCNV.x * mag
pointOfView.position.y += rotatedEyePosSCNV.y * mag
pointOfView.position.z += rotatedEyePosSCNV.z * mag
// Set PointOfView for SecondView
sceneView2.pointOfView = pointOfView
}
}
For more details look at this project on a GitHub.
No, or at least it’s possible that you will get errors all the time. It seems that when you use both an ARSCN and a ARSKVIEW View at the same time, a sensor error is presented. It may be due to privacy?

AVPlayerViewController pops entire stack of view controllers when closed

I am using an AVPlayerViewController to display video content within my application. This player can be reached via a sequence of views. The sequence is embedded in a navigation view controller. The problem I have is that whenever I close the player the entire stack of view controllers is popped from the list of view controllers in the navigation controller which means that I am sent back to my home screen (however I only want to pop the AVPlayerViewController from the list and return to the screen before). I tried to find a way to override the close Button but did not find a way. Moreover I tried to push a notification and handle it in the home screen by reinitializing the entire stack of view controllers - this solution works but does not seem like the appropriate solution. I attached the class that inherits from AVPlayerViewController and the code that implements the viewController. Thankful for any hint.
import UIKit
import AVKit
import AVFoundation
// MARK: - EduMediaVideoViewController
class EduMediaVideoViewController: AVPlayerViewController, EduMediaViewController {
// MARK: Stored Type Properties
weak var eduMediaDelegate: EduMediaElementDelegate?
var videoMedia: VideoMediaElement?
// MARK: Lifecycle Methods
override func viewDidLoad() {
super.viewDidLoad()
setUpVideo()
}
// MARK: Instance Methods
func setContent(content: MediaElement) {
guard let videoMedia = content as? VideoMediaElement else {
return
}
self.videoMedia = videoMedia
}
// MARK: Private Instance Methods
private func setUpVideo() {
let playerItem = videoMedia?.video
let player = AVPlayer(playerItem: playerItem)
self.player = player
player.play()
}
private func updateVideoProgress() {
self.eduMediaDelegate?.updateProgress(id: 1, progress: 1)
}
}
// create an extension of AVPlayerViewController
extension EduMediaVideoViewController {
// override 'viewWillDisappear'
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard let category = eduMediaDelegate?.getCategory() else {
return
}
let dict = ["category": category]
NotificationCenter.default.post(name: .kAVDismissNotification, object: nil, userInfo: dict)
}
}
extension Notification.Name {
static let kAVDismissNotification = Notification.Name.init("dismissing")
}
The code that initializes the video controller:
let eduVideoViewController = EduMediaVideoViewController()
eduVideoViewController.setContent(content: mediaElement)
eduVideoViewController.eduMediaDelegate = self
navigationController?.pushViewController(eduVideoViewController, animated: false)

Resetting ARKit coordinates

I have a simple question. If I wanted to start a game and place the board right in front of me:
gameBoard!.position = SCNVector3(0, 0, -0.6)
This works until I leave the game and come back again. Can I show the game board in exact same position in front of camera or 0.6m in front of me? I might have physically moved to another position.
If you want to reset you ARSession, you have to pause, remove all nodes and rerun your session by resetting tracking and removing anchors.
I made a reset button that does it whenever i want to reset:
#IBAction func reset(_ sender: Any) {
sceneView.session.pause()
sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
node.removeFromParentNode()
}
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
Or put it in your session was interrupted function!
This should be possible using the option resetTracking when you call run on your ARSession again.
Example:
if let configuration = sceneView.session.configuration {
sceneView.session.run(configuration,
options: .resetTracking)
}
To reset ARSession in ARKit framework is quite easy:
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
#IBOutlet var arView: ARSCNView!
#IBOutlet weak var sessionInfoLabel: UILabel!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
arView.session.run(configuration)
arView.session.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
arView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else {
return
}
let plane = Plane(anchor: planeAnchor, in: arView)
node.addChildNode(plane)
}
func sessionInterruptionEnded(_ session: ARSession) {
resetSessionTracking()
sessionInfoLabel.text = "ARSession's interruption has ended"
}
private func resetSessionTracking() {
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.vertical, .horizontal]
arView.scene.rootNode.enumerateChildNodes { (childNode, _) in
childNode.removeFromParentNode()
}
arView.session.run(config, options: [.removeExistingAnchors,
.resetTracking, ])
}
}
Hope this helps.
"This works until I leave the game and come back again."
You can't track camera position in the background. Whenever your app goes to the background and camera turns off, you're losing your position, and sessionWasInterrupted(_:) will be called.
A session is interrupted when it fails to receive camera or motion
sensing data. Session interruptions occur whenever camera capture is
not available—for example, when your app is in the background or there
are multiple foreground apps—or when the device is too busy to process
motion sensor data.