Take screenshot of ARKit view - swift

How would I insert a button and use it to take a photo and place it in photo library. I have noticed when using arkit I cant drag buttons and place them over the view. I am seen some people online say you use snapshot() for taking the photo.
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
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
}
}

I made a simple demo to show you how to combine snapshot(), ARSCNView and UIBUtton. So you may define your storyboard in this way:
as you can see, the button is inside the main view but outside and above the ARKit view
then your ViewController might be something like:
import UIKit
import ARKit
class ViewController: UIViewController {
#IBOutlet var arkitView:ARSCNView!
#IBOutlet var outputImageView:UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
arkitView.session.run(ARWorldTrackingConfiguration())
}
#IBAction func takeScreenshotAction() {
outputImageView.image = arkitView.snapshot()
}
}
final result is:

Related

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.

SwiftUI UIViewRepresentable and Custom Delegate

When creating a UIViewControllerRepresentable for SwiftUI, how do you create a Coordinator so that it can access the delegate of a third party library?
In this case, I am trying to access BBMetal, a photo-filtering library.
This is a truncated version of the code we are trying to 'bridge' to SwiftUI:
class CameraPhotoFilterVC: UIViewController {
private var camera: BBMetalCamera!
private var metalView: BBMetalView!
private var faceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
...
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
camera.start()
}...
}
extension CameraPhotoFilterVC: BBMetalCameraPhotoDelegate {
func camera(_ camera: BBMetalCamera, didOutput texture: MTLTexture) {
// do something with the photo
}
func camera(_ camera: BBMetalCamera, didFail error: Error) {
// In main thread
print("Fail taking photo. Error: \(error)")
}
}
Using UIViewRepresentable everything sets up properly and the CameraPhotoFilterVC works, starts up the camera, etc, but the extension does not respond. We tried to set this up as a Coordinator:
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraPreviewView>) -> CameraViewController {
let cameraViewController = CameraViewController()
// Throws an error because what we really want is a BBMetalCameraPhotoDelegate
//cameraViewController.delegate = context.coordinator
return cameraViewController
}
class Coordinator: NSObject, BBMetalCameraPhotoDelegate {
var parent: CameraPreviewView
init(_ parent: CameraPreviewView) {
self.parent = parent
}
func camera(_ camera: BBMetalCamera, didOutput texture: MTLTexture) {
print("do something with the photo")
}
func camera(_ camera: BBMetalCamera, didFail error: Error) {
print("Fail taking photo. Error: \(error)")
}
}
We also tried simply leaving an extension of the ViewController:
final class CameraViewController : UIViewController {
...
}
extension CameraViewController: BBMetalCameraPhotoDelegate {
func camera(_ camera: BBMetalCamera, didOutput texture: MTLTexture) {
...
}
However the delegate methods from BBMetalCameraPhotoDelegate do not 'fire.
I suppose the question is: in UIViewControllerRepresentable or UIViewRepresentable, how do you add an "external" delegate in the makeUIViewController method?
Usually, if this was say a UIPickerView, the following line would work:
picker.delegate = context.coordinator
But in this case the delegate is 'once removed'
You need to set the BBMetalCamera's delegate at some point before you use it.
You might do it immediately after creating it. You didn't show how you create it, so I don't know if that would be a good place to set it.
You could probably just do it in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
camera.photoDelegate = self
}

Show custom location with pin on Map with MapKit (Swift)

I am trying to create a view for in my app which shows the location of a restaurant. When the user enters the view, it shows something like this
But instead shows the location of the restaurant with a pin and a more zoomed in view. I am new to MapKit and have not been able to make any progress.
This is what my view consists of.
And this is what my view controller consists of.
import UIKit
import MapKit
class FD1ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
private let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
import UIKit
import MapKit
class FD1ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Create the pin location of your restaurant(you need the GPS coordinates for this)
let restaurantLocation = CLLocationCoordinate2D(latitude: 111.0, longitude: 111.0)
//Center the map on the place location
mapView.setCenter(restaurantLocation, animated: true)
}
}
This should do it. I hope it helps!

Change value of variable in GameViewController (SpriteKit Swift)

I created a UIScrollView in class GameViewController in which Subviews are scenes. When user enters a particular scene and presses a specific button ScrollView must stop scrolling.
How can I do this?
Example of code:
import SpriteKit
class GameViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
...
}
class GameScene: SKScene {
//Here we have button and if we will push it
//then scrollView.scrollEnabled must be false in class GameViewController.
Try to make new class with this code
class Data {
struct gameScene {
static var ButtonPressed:Bool = false
}
}
Then make some changes in your code
var gameScene: GameScene!
override func viewDidAppear(animated: Bool) {
gameScene = GameScene()
}
#IBAction func butPressed(sender: UIButton) {
Data.gameScene.buttonPressed = true
println("Pressed from GameViewController")
}
override func update(currentTime: CFTimeInterval) {
if (Data.gameScene.buttonPressed == true) {
println("Pressed from GameScene")
Data.gameScene.buttonPressed = false
}
}
You can call to this variable anywhere in your project with this code
Data.gameScene.buttonPressed

How to put override func before viewDidLoad()?

Open ViewController.swift for editing, and before viewDidLoad() put the code below the question into the default code of ViewController.Swift?
How do I put these 4 lines of code below using the directions above into the default code for ViewController.Swift?
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
Default code ViewController.Swift:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I'm guessing you have a storyboard in which ViewController is defined. If so, you must not override loadView():
If you use Interface Builder to create your views and initialize the view controller, you must not override this method.
(from UIViewController class reference)
Instead edit the class of the view in the storyboard and set it to WKWebView. To get the webView reference, create it as an outlet:
#IBOutlet weak var webView: WKWebView!
And link it as a referencing outlet to the view in the storyboard.
(Or you could just put the WKWebView inside the default UIView; would be simpler. You could do this either programmatically in viewDidLoad() or via the storyboard.)
Or if you don't have a storyboard, then just copypaste the code into the editor inside the class but outside the existing functions?
import UIKit
class ViewController: UIViewController {
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
However, it matters not where the func goes in the class:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
}