How to lower a UIView element in a SpriteKit scene

479 views Asked by At

I have a regular SpriteKit scene. In fact it is the default scene created from "Sprite Kit" which draws spaceships.

I would like to draw on the background.

I have successfully added a UIImageView as a subview of the main skView. I can draw on this with lines and such.

But I can't seem to succesfully put the UIImageView behind the main skview.

How can I do this? Here is my sample code.

(In the UIImageView I'm just drawing random lines every 0.1 seconds.)

@implementation GameViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Configure the view.
    SKView * skView = (SKView *)self.view;
//    skView.showsFPS = YES;
//    skView.showsNodeCount = YES;
    /* Sprite Kit applies additional optimizations to improve rendering performance */
    //skView.ignoresSiblingOrder = YES;

    // Create and configure the scene.
    GameScene *scene = [GameScene unarchiveFromFile:@"GameScene"];
    scene.scaleMode = SKSceneScaleModeAspectFill;

    [skView presentScene:scene];
}

-(void)viewWillLayoutSubviews{

    self.imageView = [UIImageView new];
    self.imageView.frame = self.view.frame;
    [self.view addSubview:self.imageView];
    [self startRepeatingTimer];
}

-(void)viewDidLayoutSubviews{

    // this doesn't work. was just trying it out.
    int i = [[self.view subviews] count];
    [self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:i-1];

}

///// DRAW RANDOM LINES EVERY 0.1 SECONDS ON THE UIIMAGEVIEW SUBVIEW
- (IBAction)startRepeatingTimer {

    [self.repeatingTimer invalidate];
    self.repeatingTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(drawOnImage:) userInfo:nil repeats:YES];
}

- (void)drawOnImage:(NSTimer*)theTimer {

    //setup
    float w = self.view.frame.size.width;
    float h = self.view.frame.size.height;

    UIGraphicsBeginImageContextWithOptions(self.view.frame.size,NO,0.0);
    [self.imageView.image drawInRect:CGRectMake(0, 0, w, h)];
    CGContextRef c = UIGraphicsGetCurrentContext();

    //draw
    CGContextMoveToPoint(c, drand48()*w,drand48()*h);
    CGContextAddLineToPoint(c, drand48()*w,drand48()*h);
    CGContextSetLineCap(c, kCGLineCapRound);
    CGContextSetLineWidth(c, 1.0f );
    CGContextSetRGBStrokeColor(c, drand48(), drand48(), drand48(), 1.0);
    CGContextStrokePath(c);

    // set the image
    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}
1

There are 1 answers

1
0x141E On BEST ANSWER

Here's a SpriteKit-friendly way to draw random lines on a background image. The basic steps are

  1. Create a blank image
  2. Convert the image to an SKTexture
  3. Create a sprite with the texture
  4. Draw a random line on the image
  5. Update the sprite's texture with the image
  6. Wait for 0.1 seconds
  7. Iterate over steps 4-6

In GameScene.m, add the following

-(void)didMoveToView:(SKView *)view {

    self.scaleMode = SKSceneScaleModeResizeFill;

    // 1. Create an empty UIImage
    self.image = [self blankImageWithSize:view.frame.size];
    // 2. Convert the image to a texture
    SKTexture *texture = [SKTexture textureWithImage:_image];
    // 3. Create a sprite from the texture
    SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:texture];

    // Center the sprite in the view
    sprite.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view..frame));
    // Set zPosition such that the sprite is underneath other nodes
    sprite.zPosition = -1000;
    [self addChild:sprite];

    self.sprite = sprite;

    // 4. and 5. Create an SKAction that draw lines on the image and then update the
    // sprite's texture
    SKAction *drawAction = [SKAction runBlock:^{
        [self drawOnImage];
        _sprite.texture = [SKTexture textureWithImage:_image];
    }];
    // 6. create a 0.1 second delay
    SKAction *wait = [SKAction waitForDuration:0.1];
    SKAction *action = [SKAction sequence:@[drawAction,wait]];

    // 7. Iterate
    [self runAction:[SKAction repeatActionForever:action]];

    // Create a label and add it to the scene to show the image is
    // in the background
    SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];

    myLabel.text = @"Hello, World!";
    myLabel.fontSize = 65;
    myLabel.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view..frame));

    [self addChild:myLabel];

}

This method creates a blank image

- (UIImage*) blankImageWithSize:(CGSize)size
{
    UIGraphicsBeginImageContext(size);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

This method draws lines on the image

- (void)drawOnImage {

    //setup
    float w = _image.size.width;
    float h = _image.size.height;

    UIGraphicsBeginImageContextWithOptions(_image.size,NO,0.0);
    [_image drawInRect:CGRectMake(0, 0, w, h)];
    CGContextRef c = UIGraphicsGetCurrentContext();

    //draw
    CGContextMoveToPoint(c, drand48()*w,drand48()*h);
    CGContextAddLineToPoint(c, drand48()*w,drand48()*h);
    CGContextSetLineCap(c, kCGLineCapRound);
    CGContextSetLineWidth(c, 1.0f );
    CGContextSetRGBStrokeColor(c, drand48(), drand48(), drand48(), 1.0);
    CGContextStrokePath(c);

    // set the image
    _image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}