Referencing a scenes display group from a custom function

I’ve not had chance to play around with scenes too much in Corona so this is probably a stupidly simple question, but…

In the standard composer functions, e.g. scene:create, you can reference self.view as the display group when adding objects. Like this:

function scene:create( event )     local sceneGroup = self.view     myButton = display.newImageRect( sceneGroup, "button-image.png", 100, 40 ) end

But what I need to do now, is swap an image like this out for another, inside a custom function that a touch event points to. The replacement image needs to be added to the same display group but self.view doesn’t seem to be a thing outside of the scene functions. I thought I could just pass sceneGroup as a parameter, like this:

function scene:create( event )     local sceneGroup = self.view     myButton = display.newImageRect( sceneGroup, "button-image.png", 100, 40 ) end function scene:show( event )     local sceneGroup = self.view     local phase = event.phase     if( phase == "did" ) then         myButton:addEventListener('touch', toggleImage(sceneGroup))     end end local function toggleImage(sceneGroup)     myButton = display.newImageRect( sceneGroup, "button-image-2.png", 100, 40 ) end

But this approach just causes error.

Surely I’m missing something obvious here?

You have a couple of issues going on.  There is a difference between calling a function and getting the address of a function.

object:addEventListener(‘touch’, addressOfThefunction) 

is different than:

object:addEventListener(‘touch’, addressOfThefunction() ) 

when you put () at the end of a function name, it executes the function at that point and returns a value. If your function doesn’t specify a value to return, then it returns nil. If you leave the () off, then you get the address of the function.  object:addEventListener() expects an address to a function, the the result of running a function, therefore you can’t do:

 myButton:addEventListener('touch', toggleImage(sceneGroup))

you have to do:
 

 myButton:addEventListener('touch', toggleImage)

The second issue is that Lua is a single pass compiler. You have to define things before you use them. If your code is in the order listed above, you’re really calling:

 myButton:addEventListener('touch', nil(sceneGroup))

Which isn’t correct and would likely cause an error. You need to move the toggleImage() function higher up in the module, somewhere before scene:create().

Now there is the issue of getting the scene group since you can’t pass it as a value in the addEventListener.

You have a couple of options.  First, and probably the easiest is to understand that “scene” is a visible object since it was defined as a local at the top of the scene. If you look at functions like scene:create() it accesses self.view. Because :create() is a method of the object scene, self = scene. Since this is true, you could in toggleImage() simply write:

local function toggleImage() local sceneGroup = scene.view myButton = display.newImageRect( sceneGroup, "button-image-2.png", 100, 40 ) end

If your scene isn’t overly complex, this should work like a charm, however, scene.view inside of toggeImage() is a Lua “upValue”. Lua limits you to 60 upValues which shouldn’t be a limit that many people would hit. The proper way to avoid any problems is to make your toggleImage() a method of the scene group:

function scene:toggleImage() local sceneGroup = self.view myButton = display.newImageRect( sceneGroup, "button-image-2.png", 100, 40 ) end

Rob

Thanks Rob,

Ah - I’d forgotten about the parenthesis changing the way a function is referenced like that. Lua is still somewhat alien to me with that one!

My functions aren’t actually in that order, no. The above code is all simplified and somewhat pseudo for the sake of examplifying (is that a word?) what I’m trying to achieve :) .

Both of your examples for handling the scene group make sense. I hadn’t realised I could create custom scene functions like that, and frankly I’ve no idea why I didn’t think to call scene.view before  :huh: , but alas I’ve hit another snag…

I’d tried the scene attachment method first, initiating my custom function as function scene:toggleImage() and calling self.toggleImage as the touch event, but this couldn’t then see what myButton was (it’s defined as a local variable at the top of the script so I suppose outside of the ‘scene’?). So I then tried your simpler method of just calling scene.view within the function, which technically does work, but rather than replacing the already rendered myButton, it’s just creating a new instance of it?

Presumably I’m now declaring the button itself outside of the scenes variable scope and need to change that for either of these methods to work?

regardless of how/where you do your toggling, you’ll first want to remove the previous image before recreating a new one.  (aside, a sprite might make this simpler - just change frames instead of recreating, fwiw)

Ah… I think I see what’s going on now. I’m forgetting that the display group is essentially just a table aren’t I? My code is indeed changing the myButton variable to hold a new image and not just creating a new instance, but when I previously added that to the display group I was actually cloning what the variable held into the display groups table, so now need to clear that out in addition to updating the variable?

I can see it taking me another few years to stop being an idiot with variable scope in Lua!

You have a couple of issues going on.  There is a difference between calling a function and getting the address of a function.

object:addEventListener(‘touch’, addressOfThefunction) 

is different than:

object:addEventListener(‘touch’, addressOfThefunction() ) 

when you put () at the end of a function name, it executes the function at that point and returns a value. If your function doesn’t specify a value to return, then it returns nil. If you leave the () off, then you get the address of the function.  object:addEventListener() expects an address to a function, the the result of running a function, therefore you can’t do:

 myButton:addEventListener('touch', toggleImage(sceneGroup))

you have to do:
 

 myButton:addEventListener('touch', toggleImage)

The second issue is that Lua is a single pass compiler. You have to define things before you use them. If your code is in the order listed above, you’re really calling:

 myButton:addEventListener('touch', nil(sceneGroup))

Which isn’t correct and would likely cause an error. You need to move the toggleImage() function higher up in the module, somewhere before scene:create().

Now there is the issue of getting the scene group since you can’t pass it as a value in the addEventListener.

You have a couple of options.  First, and probably the easiest is to understand that “scene” is a visible object since it was defined as a local at the top of the scene. If you look at functions like scene:create() it accesses self.view. Because :create() is a method of the object scene, self = scene. Since this is true, you could in toggleImage() simply write:

local function toggleImage() local sceneGroup = scene.view myButton = display.newImageRect( sceneGroup, "button-image-2.png", 100, 40 ) end

If your scene isn’t overly complex, this should work like a charm, however, scene.view inside of toggeImage() is a Lua “upValue”. Lua limits you to 60 upValues which shouldn’t be a limit that many people would hit. The proper way to avoid any problems is to make your toggleImage() a method of the scene group:

function scene:toggleImage() local sceneGroup = self.view myButton = display.newImageRect( sceneGroup, "button-image-2.png", 100, 40 ) end

Rob

Thanks Rob,

Ah - I’d forgotten about the parenthesis changing the way a function is referenced like that. Lua is still somewhat alien to me with that one!

My functions aren’t actually in that order, no. The above code is all simplified and somewhat pseudo for the sake of examplifying (is that a word?) what I’m trying to achieve :) .

Both of your examples for handling the scene group make sense. I hadn’t realised I could create custom scene functions like that, and frankly I’ve no idea why I didn’t think to call scene.view before  :huh: , but alas I’ve hit another snag…

I’d tried the scene attachment method first, initiating my custom function as function scene:toggleImage() and calling self.toggleImage as the touch event, but this couldn’t then see what myButton was (it’s defined as a local variable at the top of the script so I suppose outside of the ‘scene’?). So I then tried your simpler method of just calling scene.view within the function, which technically does work, but rather than replacing the already rendered myButton, it’s just creating a new instance of it?

Presumably I’m now declaring the button itself outside of the scenes variable scope and need to change that for either of these methods to work?

regardless of how/where you do your toggling, you’ll first want to remove the previous image before recreating a new one.  (aside, a sprite might make this simpler - just change frames instead of recreating, fwiw)

Ah… I think I see what’s going on now. I’m forgetting that the display group is essentially just a table aren’t I? My code is indeed changing the myButton variable to hold a new image and not just creating a new instance, but when I previously added that to the display group I was actually cloning what the variable held into the display groups table, so now need to clear that out in addition to updating the variable?

I can see it taking me another few years to stop being an idiot with variable scope in Lua!