2

I am trying to add a background image in my view controller so my sprite kit game scenes can be in the foreground. It's important for me for my background image to stay on screen while the game scenes transition to each other. The problem I am having is how to place the subview I created for my background to be behind my skscene, at the moment my background image is in front of my game scene and all you can see is the image sendSubviewToBack doesn't seem to work. The following is my viewDidLoad Thanks

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {

        super.viewDidLoad()
        let scene = MainMenuScene(size: CGSize(width: 1536, height: 2048))
        let skView = self.view as! SKView

        scene.scaleMode = .AspectFill
        skView.presentScene(scene)

        let backgroundImageView = UIImageView(image: UIImage(named: "bg.jpg"))
        backgroundImageView.frame = view.frame
        backgroundImageView.contentMode = .ScaleAspectFill
        view.addSubview(backgroundImageView)
        view.sendSubviewToBack(backgroundImageView)

    }
}
user1763430
  • 77
  • 1
  • 7
  • The view of your view controller is the SKView. All its subviews are drawn after it has drawn itself (the current scene). Better use a regular UIView as the view of your view controller, add the background UIImageView and on top of that add your SKView. Or even better, use SpriteKit for your background view as described below in the accepted answer. – Raginmari Feb 04 '16 at 07:50
  • Refer this https://stackoverflow.com/questions/18894493/making-a-skscenes-background-transparent-not-working-is-this-a-bug/24494346#24494346 – Suresh Oct 05 '16 at 14:42

5 Answers5

2

What you're describing can't be done with UIKit because your view can't be visible behind the SKView which renders your scene.

In iOS 8+ you can use the SKView's allowsTransparency property. Documentation states:

This property tells the drawing system as to how it should treat the view. If set to NO, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to YES, the drawing system composites the view normally with other content. The default value of this property is NO.

But in my opinion this is not the best solution. The best solution is to use Sprite Kit, but the problem is you can't preserve nodes across scenes. You must add the node again during each scene transition. This is why I do not use Apple's scene transitions the way the documentation describes. Instead I use my own custom SKNodes and make my own custom transitions using those nodes, which I highly recommend you do. I gave a detailed answer about this here

Community
  • 1
  • 1
Epic Byte
  • 33,840
  • 12
  • 45
  • 93
  • Thanks for your answer, I found this and your other answer very helpful. I understand exactly what you are describing but my coding is still quite basic, do you have any links to any good tutorials about subclassing sknodes and layering them in this way? – user1763430 Jun 22 '15 at 09:18
  • @user1763430 Unfortunately not. However I can post some sample code detailing how it's done. Maybe that will clear things up. I'm a little busy today but perhaps by tonight or tomorrow I can post the code. I'll let you know when I post it. – Epic Byte Jun 22 '15 at 12:34
  • Thank you, that will be incredibly useful – user1763430 Jun 22 '15 at 13:27
  • @user1763430 I have added the code to the answer I linked in my answer. Here is the link again (http://stackoverflow.com/a/29372723/2158465). If you feel your question has been answered you should accept this answer. Good luck with your game and feel free to message me (either on this answer or the one I linked) if you have any questions. Good luck! – Epic Byte Jun 22 '15 at 14:42
2

I dealt with the same issue, and here is how to fix it:

  1. Put the code in viewWillLayoutSubviews(), not in viewDidLoad() - this will ensure you have access to all the view's properties.
  2. Don't cast the self.view as an SKView, leave it as an UIView and instead create two child views: one for the background image and one for the SKScene

Here is the corrected code:

var skView : SKView? = nil

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    if (skView == nil) {
        let backgroundImageView = UIImageView(image: UIImage(named: "bg.jpg"))
        view.addSubview(backgroundImageView)

        skView = SKView(frame: CGRect(origin: CGPointZero, size: CGSize(width: 1536, height: 2048)))
        skView!.ignoreSiblingOrder = true
        let scene = MainMenuScene(size: CGSize(width: 1536, height: 2048))
        scene.scaleMode = .AspectFill

        view.addSubView(skView!)
        skView!.presentScene(scene)
    }
}

My particular reason to do this was so I could have a game that would be playable on both iPhones and iPads without aspect ratio distortion. The 'playable area' is always 16:9, and when seen on an iPad, you see a background image that extends to the sides of the playable area.

What I did was to load a background image meant for the iPad (which has an aspect ratio of 4:3), defined the skView to have an aspect ratio of 16:9 (So instead of 1536/2048 I am using 750/1334, for example) and set the scene's scaleMode to .AspectFit. When seen on an iPhone, the scene covers the background completely. When seen on an iPad, you can see the background image complement the sides of the playable area.

Hope this helps!

1

Make a SKSpriteNode with the background texture!

background.size = self.frame.size //Make the background as big as the screen
Aryaman Goel
  • 538
  • 1
  • 4
  • 15
  • I am doing this temporarily in my SKScene's but that requires me to redefine it in every scene I make. I would like to define it once and for all by layering views in the view controller but i'm not too sure how. I can't create an SKSpriteNode in viewDidLoad because the node has to be a child of a scene. Unless I am missing something? – user1763430 Jun 22 '15 at 13:35
  • that is how you would do it in game development cause the background is like a character itself – Aryaman Goel Jun 22 '15 at 13:56
  • Or you could also drag a UIImageView into the scene from main.storyboard – Aryaman Goel Jun 22 '15 at 13:57
0

Generally UI elements are placed on top of each other depending on the order they were added to the view.

If you want to customize that you can use the property .layer.zPosition which is any value you pick. The lowest value is in the very back while the highest one is on top.

Hope that helps :)

LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
  • Thanks for the reply, do you have any examples of how I could modify my code? I'm not sure which properties here have the option of a zPosition extension – user1763430 Jun 20 '15 at 19:54
  • Well if you have an imageView for example you can do `imgView.zPosition = 1`. And then the sceneView could be at a 2, so above it. – LinusGeffarth Jun 20 '15 at 19:56
  • None of the elements in my code have the option to modify the z position. Eg. backgroundImageView.zPosition throws me an error. I am making my entire UI programmatically in sprite kit which is being called in the view controller as let scene – user1763430 Jun 21 '15 at 07:15
  • Oh sorry. It is `imageView.layer.zPosition`. – LinusGeffarth Jun 21 '15 at 07:18
  • Still same problem, the background is drawn in front of the game and not behind it. Is it possible that swift does not treat my skView as a UIView element? – user1763430 Jun 21 '15 at 08:21
  • Well an `SKView` is of course no `UIView` element, it's a separate view. But if you added the `SKView` to the `UIView` then that should work I think. – LinusGeffarth Jun 21 '15 at 08:23
0

You can use insertSubView: instead of addSubView and specify the index, which determines z-order of views. A view with a higher index lies above those with lower indices.

Rod
  • 789
  • 7
  • 19
  • Thanks for your comment. – user1763430 Jun 21 '15 at 08:19
  • I tried view.insertSubview(backgroundImageView, belowSubview: skView) but its still drawing the background in front of the game – user1763430 Jun 21 '15 at 08:20
  • Maybe i need to declare skView as another subview first? since the function is asking for another subview as argument. But if so, then surely I would have gotten an error from the compiler – user1763430 Jun 21 '15 at 08:23
  • Maybe your problem lies here: http://stackoverflow.com/questions/25671999/difference-between-a-scene-and-a-view-in-ios – Rod Jun 21 '15 at 18:18