Change scene from external module

I am trying to create a navigation menu history button that persists throughout an app similar to the Android action bar. As the user goes through the app, the navigation menu’s history button will change its target. For example…

menu.lua -> no history / back button

scene1.lua -> history goes back to menu.lua on “tap”

scene2.lua -> history goes back to scene1.lua on “tap”

I’d like to control the history. Where I’m having a problem is switching scenes from my external module I’ve created which is called in every scene.

I set up an environment.lua file as follows (trimmed down for readability):

--environment.lua local Environment = {} local navBar local function new( backTo ) local backTo = backTo or nil navBar = display.newGroup() local btn = display.newRect( 0, 0, 44, 44 ) navBar:insert( btn ) function navBar:tap( event ) if( backTo ) then --Go to scene defined in backTo end end navBar:addEventListener( "tap", navBar ) end Environment.new = new return Environment

In my scene1.lua file, I call the above with:

--scene1.lua local environment = require( "environment" ) function scene:enterScene( event ) local view = self.view environment.new( "menu" ) end function scene:exitScene( event ) local view = self.view environment.destroy() --removes and nils the navBar package.loaded["environment"] = nil end

(I also have a destroy function and unload the package in each scene.) This had zero memory leaks and worked perfectly printing to the console on the tap…however, when I try to require the storyboard and then gotoScene in the tap function, I start leaking memory, scenes don’t purge, and I am getting double (and then some) tap events as though the navBar is loading on top of itself from scene to scene.

How can I change the scene using a button loaded from this external module? 

Here is my recommendation.  Let me know if you have any specific questions.  The doubling of your nav is caused by not inserting it into the storyboard scene ( self.view), which is resolved by passing the navBar back to the scene.  The other issue is resolved by using a custom event.

In environment.lua

[lua]
–< environment.lua

local Environment = {}

local navBar

Environment.new = function( backTo )

  

  local backTo = backTo or nil

  navBar = display.newGroup()

  local btn = display.newRect( 0, 0, 44, 44 )

  navBar:insert( btn )

  local function onTap( event )

    if( backTo ) then

    

      --dispatch an event to scene

      local e = {

        name = “changeScene”,

        backTo = backTo,

      }

      

      navBar:dispatchEvent( e )

      return true

    end

  end

  navBar:addEventListener( “tap”, onTap )

  return navBar

end

return Environment
[/lua]

In scene1.lua

[lua]
–< SCENE 1

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

local environment = require( “environment” )

function scene:enterScene( event )

  local view = self.view

  

  local grp = environment.new( “menu” )

  

  local onBackBtn = function( event )

    if ( event.name == “changeScene” ) then

      

      print( event.backTo )

      

      --CHANGE SCENE HERE

      storyboard.gotoScene( event.backTo )

      

      return true

    end

  end

  grp:addEventListener( “changeScene”, onBackBtn )

  

  view:insert( grp )

  

end

function scene:exitScene( event )

  local view = self.view

  --environment.destroy() --removes and nils the navBar

  package.loaded[“environment”] = nil

end

– “enterScene” event is dispatched whenever scene transition has finished

scene:addEventListener( “enterScene”, scene )

– “exitScene” event is dispatched before next scene’s transition begins

scene:addEventListener( “exitScene”, scene )

return scene
[/lua]

Hope that helps.

Cheers.

Thanks develephant. I was trying to avoid having to script any of the navBar in scene1.lua at all, beyond environment.new( “scene name”) but I’m assuming, based on your suggestion, that’s not possible?

To my knowledge, though someone else might be able to suggest a different method.

Thanks!

I’ll play around with a few other ideas.  You may be able to pass the scene to your Environment.new( “menu”, scene ) to add the display object without doing it on the scene itself, using scene.view.

Cheers.

Here is another method that seems to be working, though I’ve done no memory tests.

In environment.lua

[lua]
–< environment.lua

local storyboard = require( “storyboard” )

local Environment = {}

local navBar

Environment.new = function( backTo, view )

  

  local backTo = backTo or nil

  local view = view

  navBar = display.newGroup()

  local btn = display.newRect( 0, 0, 44, 44 )

  navBar:insert( btn )

  local function onTap( event )

    if( backTo ) then

        storyboard.gotoScene( backTo )

        return true

    end

  end

  navBar:addEventListener( “tap”, onTap )

  view:insert( navBar )

end

return Environment

[/lua]

In scene1.lua

[lua]
–< SCENE 1

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

local environment = require( “environment” )

function scene:enterScene( event )

  local view = self.view

  

  environment.new( “menu”, view )

end

function scene:exitScene( event )

  local view = self.view

  --environment.destroy() --removes and nils the navBar

  package.loaded[“environment”] = nil

end

– “enterScene” event is dispatched whenever scene transition has finished

scene:addEventListener( “enterScene”, scene )

– “exitScene” event is dispatched before next scene’s transition begins

scene:addEventListener( “exitScene”, scene )

return scene

[/lua]

Cheers.

That did it. Thanks so much! But I am curious - why did you get rid of the destroy in the exit scene? Not necessary because of the unload? Just curious. Thanks so much for your help!

Well, I commented it out because I don’t have an example of that method in your code. :)  If you post your destroy method I can determine if it’s needed or not.

Cheers.

Got it. It is:

local function destroy() if( navBar ) then navBar:removeSelf() navBar = nil end return true end Environment.destroy = destroy

I added it back in and that plugged a small memory leak. Thanks again for your help!

Not a problem.  Glad it got worked out.

Cheers.

Here is my recommendation.  Let me know if you have any specific questions.  The doubling of your nav is caused by not inserting it into the storyboard scene ( self.view), which is resolved by passing the navBar back to the scene.  The other issue is resolved by using a custom event.

In environment.lua

[lua]
–< environment.lua

local Environment = {}

local navBar

Environment.new = function( backTo )

  

  local backTo = backTo or nil

  navBar = display.newGroup()

  local btn = display.newRect( 0, 0, 44, 44 )

  navBar:insert( btn )

  local function onTap( event )

    if( backTo ) then

    

      --dispatch an event to scene

      local e = {

        name = “changeScene”,

        backTo = backTo,

      }

      

      navBar:dispatchEvent( e )

      return true

    end

  end

  navBar:addEventListener( “tap”, onTap )

  return navBar

end

return Environment
[/lua]

In scene1.lua

[lua]
–< SCENE 1

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

local environment = require( “environment” )

function scene:enterScene( event )

  local view = self.view

  

  local grp = environment.new( “menu” )

  

  local onBackBtn = function( event )

    if ( event.name == “changeScene” ) then

      

      print( event.backTo )

      

      --CHANGE SCENE HERE

      storyboard.gotoScene( event.backTo )

      

      return true

    end

  end

  grp:addEventListener( “changeScene”, onBackBtn )

  

  view:insert( grp )

  

end

function scene:exitScene( event )

  local view = self.view

  --environment.destroy() --removes and nils the navBar

  package.loaded[“environment”] = nil

end

– “enterScene” event is dispatched whenever scene transition has finished

scene:addEventListener( “enterScene”, scene )

– “exitScene” event is dispatched before next scene’s transition begins

scene:addEventListener( “exitScene”, scene )

return scene
[/lua]

Hope that helps.

Cheers.

Thanks develephant. I was trying to avoid having to script any of the navBar in scene1.lua at all, beyond environment.new( “scene name”) but I’m assuming, based on your suggestion, that’s not possible?

To my knowledge, though someone else might be able to suggest a different method.

Thanks!

I’ll play around with a few other ideas.  You may be able to pass the scene to your Environment.new( “menu”, scene ) to add the display object without doing it on the scene itself, using scene.view.

Cheers.

Here is another method that seems to be working, though I’ve done no memory tests.

In environment.lua

[lua]
–< environment.lua

local storyboard = require( “storyboard” )

local Environment = {}

local navBar

Environment.new = function( backTo, view )

  

  local backTo = backTo or nil

  local view = view

  navBar = display.newGroup()

  local btn = display.newRect( 0, 0, 44, 44 )

  navBar:insert( btn )

  local function onTap( event )

    if( backTo ) then

        storyboard.gotoScene( backTo )

        return true

    end

  end

  navBar:addEventListener( “tap”, onTap )

  view:insert( navBar )

end

return Environment

[/lua]

In scene1.lua

[lua]
–< SCENE 1

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

local environment = require( “environment” )

function scene:enterScene( event )

  local view = self.view

  

  environment.new( “menu”, view )

end

function scene:exitScene( event )

  local view = self.view

  --environment.destroy() --removes and nils the navBar

  package.loaded[“environment”] = nil

end

– “enterScene” event is dispatched whenever scene transition has finished

scene:addEventListener( “enterScene”, scene )

– “exitScene” event is dispatched before next scene’s transition begins

scene:addEventListener( “exitScene”, scene )

return scene

[/lua]

Cheers.

That did it. Thanks so much! But I am curious - why did you get rid of the destroy in the exit scene? Not necessary because of the unload? Just curious. Thanks so much for your help!

Well, I commented it out because I don’t have an example of that method in your code. :)  If you post your destroy method I can determine if it’s needed or not.

Cheers.

Got it. It is:

local function destroy() if( navBar ) then navBar:removeSelf() navBar = nil end return true end Environment.destroy = destroy

I added it back in and that plugged a small memory leak. Thanks again for your help!