In ARKit, we can easily add an SKScene overlay as a HUD (head up display).
let hud = SKScene(size: sceneView.frame.size)
sceneView.overlaySKScene = hud
How do you do this in RealityKit? As there are no overlaySKScene in ARView?
You can create this overlay programmatically:
import SpriteKit
import RealityKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
let skView = SKView(frame: CGRect(x: 0, y: 0, width: 200, height: 150))
let scene = SKScene()
override func viewDidLoad() {
super.viewDidLoad()
arView.backgroundColor = .black
skView.showsFPS = true
scene.backgroundColor = .systemTeal
let boxAnchor = try! Experience.loadBox()
boxAnchor.steelBox?.scale = [9,9,9]
arView.scene.anchors.append(boxAnchor)
arView.addSubview(skView)
skView.presentScene(scene)
}
}
Related
Apple's documentation states I can't change the position using the position getter/setter. I've tried the anchor point suggestion but still unable to move my scene's position in Scene Editor to the top. Does anyone have any ideas?
Implementation has been done via Scene Editor, currently the only code submitted to help scene present is below:
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Load 'GameScene.sks' as a GKScene. This provides gameplay related content
// including entities and graphs.
if let scene = GKScene(fileNamed: "GameScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! GameScene? {
// Copy gameplay related content over to the scene
sceneNode.entities = scene.entities
sceneNode.graphs = scene.graphs
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFit
print(sceneNode.anchorPoint)
print(sceneNode.position)
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
}
In AppDelegate bypass storyboard.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = GameViewController()
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
In GameViewController
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(skView)
setupSKView()
}
let skView: SKView = {
let view = SKView()
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .fill
view.presentScene(scene)
}
return view
}()
func setupSKView(){
skView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height / 2)
}
}
I try to make a view controller, where i.e top half of the screen is SKScene, and bottom half of the screen is normal scene, I mean with buttons etc. Is this possible? I created a Single View application, and I am adding some code snippets below, which is the code from viewcontroller.swift file. But it doesn't create boxes to the tapped locations and also it doesn't move as expected a free fall motion when tapped. Maybe is there an issue about that I created a Single view application rather than choosing Game application when first creating the project? Maybe Metal enable/disable needed? I am not professional, therefore just guesses.
import UIKit
import SwiftUI
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let box = SKSpriteNode(color: .red, size: CGSize(width: 0.04, height: 0.04))
box.physicsBody = SKPhysicsBody(rectangleOf: box.frame.size)
box.position = location
addChild(box)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
scene.backgroundColor = .clear
let transition = SKTransition.fade(withDuration: 1)
view.presentScene(scene, transition: transition)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.allowsTransparency = true
view.backgroundColor = UIColor.clear
}
}
}
i want to implement a singleton spinner, because any app that has a api needs a spinner. well I can code a spinner custom, but my problem is that I should code the next line ever if I want to show it.
let rosetaGIF = UIImage(named: "wheel.png")
let ind = MyIndicator(frame: CGRect(x: 0, y: 0, width: (rosetaGIF?.size.width)!/2, height: (rosetaGIF?.size.height)!/2), image: rosetaGIF!)
view.addSubview(ind)
view.alpha = 0.5
ind.startAnimating()
that's not good, because I must put this lines every time that I want to show the spinner, well, my spinner class is the next. I'm using swift 4.2
import UIKit
class MyIndicator: UIActivityIndicatorView {
let loadingView = UIView(frame: (UIApplication.shared.delegate?.window??.bounds)!)
let imageView = UIImageView()
let sizeView = UIViewController()
init(frame: CGRect, image: UIImage) {
super.init(frame: frame)
imageView.frame = bounds
imageView.image = image
imageView.contentMode = .scaleAspectFit
imageView.center = CGPoint(x: sizeView.view.frame.width/2, y: sizeView.view.frame.height/2)
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(imageView)
}
required init(coder: NSCoder) {
fatalError()
}
override func startAnimating()
{
isHidden = false
rotate()
}
override func stopAnimating()
{
isHidden = true
removeRotation()
}
private func rotate() {
let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.toValue = NSNumber(value: Double.pi * 1)
rotation.duration = 1
rotation.isCumulative = true
rotation.repeatCount = Float.greatestFiniteMagnitude
self.imageView.layer.add(rotation, forKey: "rotationAnimation")
}
private func removeRotation() {
self.imageView.layer.removeAnimation(forKey: "rotationAnimation")
}
}
what should I do for that the spinner will be singleton?
thanks
I suggest you create a util class and make that class Singleton.
import UIKit
class Util {
static let shared = Util()
private init(){}
var loader: MyIndicator?
func showLoader(view: UIView){
hideLoader()
let rosetaGIF = UIImage(named: "wheel.png")
loader = MyIndicator(frame: CGRect(x: 0, y: 0, width: (rosetaGIF?.size.width)!/2, height: (rosetaGIF?.size.height)!/2), image: rosetaGIF!)
view.addSubview(loader!)
view.alpha = 0.5
loader?.startAnimating()
}
func hideLoader(){
loader?.stopAnimating()
loader?.removeFromSuperview()
}
}
How to use this class
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Util.shared.showLoader(view: view) // for showing the loader
Util.shared.hideLoader() // for hiding the loader
}
}
Create a Base Controller with your spinner, then inherit other Controllers from you Base Controller and show the spinner whenever you want.
class BaseViewController: UIViewController {
var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: (UIScreen.main.bounds.width-40)/2, y: (UIScreen.main.bounds.height-40)/2, width: 40, height: 40))
activityIndicator.transform = CGAffineTransform(scaleX: 2, y: 2)
activityIndicator.color = .darkGray
view.addSubview(activityIndicator)
}
}
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimating() // you can show the spinner wherever you want.
}
}
hope this will help to you.
I am trying to show this simple ViewController in iPad Playgrounds with this code:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width/2, height: 100))
view1.backgroundColor = .red
view.addSubview(view1)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
You can see that, even though it says view.frame.width/2 for the frame of View1, the view still looks like this:
What is wrong? Thank you.
This is the solution I used which got it to work. In Swift Playgrounds, the layout is created in the viewDidLayoutSubview method so all frames should be created there.
You can set your preferred size after you init your ViewController:
import UIKit
import PlaygroundSupport
class VC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width/2, height: 100))
view1.backgroundColor = .red
view.addSubview(view1)
}
}
let vc = VC()
vc.preferredContentSize = CGSize(width: 768, height: 1024)
PlaygroundPage.current.liveView = vc
Playground with preferredContentSize
Playground without preferredContentSize
I have tried various different ways to do this and nothing works. Here's my code. If I change to a UIColor instead of using patternImage it works fine. I just want a static image as the background. I appreciate any advice I can get! Thanks!
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
scene.backgroundColor = UIColor(patternImage: UIImage(named: "IMG_6999")!)
skView.presentScene(scene)
}
You are setting background color, not an image.
In didMove(to view:) of GameScene.swift you need to create SpriteNode that will be contain your image. Then add this node to the scene.
For example:
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.zPosition = -1 //other nodes will be higher then background node
background.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(background)
}
}