5

How can I access the image filename from an SKSpriteNode? I have been trying to Google this, but no answer seems to be available. Here is the impotent part of my code:

current_ingredient?.name = String(describing: current_ingredient?.texture)

print("Filename: \(current_ingredient?.name)")

The print command returns this:

Filename: Optional("Optional(<SKTexture> \'ingredient14\' (110 x 148))")

So, the question is, how do I get only "ingredient14"?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
thar
  • 1,094
  • 2
  • 13
  • 25

3 Answers3

9

Saving inside userData

You can store the image name you used to create a sprite inside the userData property.

let imageName = "Mario Bros"
let texture = SKTexture(imageNamed: imageName)
let sprite = SKSpriteNode(texture: texture)
sprite.userData = ["imageName" : imageName]

Now given a sprite you can retrive the name of the image you used originally

if let imageName = sprite.userData?["imageName"] as? String {
    print(imageName) // "Mario Bros"
}
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • 3
    This seems like most safest way because if Apple decide to change an implementation of description property, the other solution with extensions will break and re-write (update) would be needed. I really wonder why texture name is not made as a property from the start. It would be useful. – Whirlwind Dec 22 '16 at 16:05
  • I agree this is best, got my upvote. Only downfall is userData is mutable, so it is possible to accidentally overwrite it. Perhaps a static lookup table would work better. – Knight0fDragon Dec 22 '16 at 16:43
  • @Knight0fDragon: I just added an update where the image name is saved inside a Singleton. – Luca Angeletti Dec 22 '16 at 16:50
  • 1
    hmmmm, I don't think saving the SKSpriteNode is a good idea, that will end up retaining the sprite. And we can't use the SKTexture either because apple no longer pulls the same object in an SKTexture object – Knight0fDragon Dec 22 '16 at 17:02
  • @Whirlwind, do any of you guys know if the CGimage of an SKTexture gets shared between multiple SKTexture instances pulled from the atlas? Perhaps that could be use that the key. – Knight0fDragon Dec 22 '16 at 17:16
  • @Knight0fDragon I can check that with others but personally I haven't tested something like that... – Whirlwind Dec 22 '16 at 17:19
  • @Knight0fDragon I just briefly tested this, but can't be certain ... texture.cgImage() will in one case point to the same memory address, and in another case, it will point to the different memory address. In the case where it points to the different memory address, I was testing like you did [here](http://stackoverflow.com/a/37119708/3402095). The memory address was the same when I was declaring a new variable for a texture every time... So instead of var texture = some texture, and then texture = the same texture, I did var texture = some texture, var texture2 = the same texture. – Whirlwind Dec 22 '16 at 21:25
  • Also, in the case when I was getting different memory addresses like in your question, I have noticed that memory consumption stays the same. And if you have a time, take a look at this. One of SKA members wrote this. It is a bit old, but some good research is done : http://stackoverflow.com/a/37119708/3402095 – Whirlwind Dec 22 '16 at 21:25
  • Yeah, that answer needs some update for iOS 10 since apple decided to change it again lol. Duh, why don't we just use the description as the key, if this changes in the future it won't matter because the new variation will just be the key. – Knight0fDragon Dec 22 '16 at 21:26
  • As far as memory consumption is concerned it would stay the same, because it is destroying the old SKTexture instance and making a new one – Knight0fDragon Dec 22 '16 at 21:27
  • @Knight0fDragon I thought about that. It sounds unperformant. – Whirlwind Dec 22 '16 at 21:28
  • well at the SKTexture level this would be a pain, but at the sprite level it would work great – Knight0fDragon Dec 22 '16 at 21:28
  • I updated [that answer](http://stackoverflow.com/a/37119708/3402095) @Whirlwind linked above, but the only change was to mention that textures taken from a pointer-identical atlas are no longer pointer-identical. Under iOS9 there were a number of ways to end up with two different SKTextures which shared the same bulky image data; now in iOS10 there is one more way. I agree it would be nice to access the image filename, since that must ultimately identify the "bulky image data" in memory, too. But no, I think we must associate our own tag/name/key with it, whether through userData or a cache. – Karl Voskuil Dec 23 '16 at 18:31
4

It is stored in the description, so here is a nice little extension I made to rip it out.

extension SKTexture
{
    var name : String
    {
        return self.description.slice(start: "'",to: "'")!
    }
}

extension String {
    func slice(start: String, to: String) -> String? 
    {

        return (range(of: start)?.upperBound).flatMap 
        { 
          sInd in
            (range(of: to, range: sInd..<endIndex)?.lowerBound).map 
            {
                eInd in
                substring(with:sInd..<eInd)

            }
        }
    }
}
usage:

print(sprite.texture!.name)
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44
  • 1
    This will work, but what if Apple change an implementation of description property? I mean today, updates go fast, but still... – Whirlwind Dec 22 '16 at 16:10
  • @Whirlwind, there is no other way to grab the texture name from the SKTexture as far as I can tell, only thing I can say is watch the Beta's and keep up to date – Knight0fDragon Dec 22 '16 at 16:17
  • Yeah, no other way. – Whirlwind Dec 22 '16 at 16:18
  • Although, could probably try to future proof the extension a little bit – Knight0fDragon Dec 22 '16 at 16:19
  • @KnightOfDragon, this works perfectly for the project I work on, so thanks a lot for your time. – thar Dec 23 '16 at 19:20
  • @Knight0fDragon will it be possible to make an add on to the above, so it also possible to get the name from the description on bodyB object? Print command return this: type: representedObject:[ name:'(null)' texture:[ 'ingredient14' (110 x 148)] position:{0, -160.33201599121094} scale:{1.00, 1.00} size:{110, 148} anchor:{0.5, 0.5} rotation:0.00] – thar Dec 23 '16 at 19:46
  • 1
    you do body.node.name to get the name of the node attached to the body – Knight0fDragon Dec 23 '16 at 19:53
  • @Knight0fDragon, thanks a lot for this very helpful and fast response! :) – thar Dec 23 '16 at 19:59
3

You can extract the file name from the texture's description with the following extension

extension SKTexture {
    var name:String? {
        let comps = description.components(separatedBy: "'")
        return comps.count > 1 ? comps[1] : nil
    }
}

Usage:

if let name = sprite.texture?.name {
    print (name)
}
Epsilon
  • 1,016
  • 1
  • 6
  • 15