composer.gotoScene() appears to be called multiple times

This is my first post so please be gentle…

I have an app (using the term loosely) that I have been using to learn some of the different functions of corona. I started from the composer scene template and I have been learning different aspects of corona and implementing them into this “app”. It has been going nice and easy for the most part, however, now when I cycle through the different scenes, it works fine until about the 3rd or 4th time through and then it starts calling gotoScene() multiple times (I know this because I have set print statements to echo this result). Is there any reason this should be happening?

Could you post your code for the scene that is calling the gotoScene() multiple times?

Use the ‘<>’ icon on the toolbar to paste your code in the correct format.  If you just paste your code into the reply screen directly it will not format well and be hard to read.

My first guess from the little info provided is you have an eventListener that is not getting removed when you leave a scene and each return to that scene creates an additional eventListener that is calling the ‘gotoScene()’ …  but I am just guessing.  If you can post the code someone on the forum will likely figure it out quickly.

Bob

Look for button event handlers where you might not be handling both the “began” and “ended” phases? Look for collisions that could be triggering multiple times.

Rob

--------------------------------------------------------------------------------- -- -- scene1.lua -- --------------------------------------------------------------------------------- local sceneName = ... local composer = require( "composer" ) -- Load scene with same root filename as this file local scene = composer.newScene( sceneName ) --------------------------------------------------------------------------------- local nextSceneButton local selectNewLogo local logo --local userLogoExists = false --local logoGroup function scene:create( event ) local sceneGroup = self.view -- Called when the scene's view does not exist -- -- INSERT code here to initialize the scene -- e.g. add display objects to 'sceneGroup', add touch listeners, etc end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then trumpFace = self:getObjectByName("trump") trumpFace.isVisible = false -- Called when the scene is still off screen and is about to move on screen local logoPath = system.pathForFile( "userLogo.jpg", system.DocumentsDirectory ) local logoFile = io.open(logoPath) if logoFile then --trumpFace.isVisible = false logo = nil logo = display.newImage("userLogo.jpg", system.DocumentsDirectory) io.close(logoFile) logoFile = nil logo:scale(.15,.15) logo.x = display.contentWidth/2 logo.y = display.contentHeight/2 logo.new = true else trumpFace.isVisible = true logo = trumpFace logo.new = false end sceneGroup:insert(logo) goBtn = self:getObjectByName("goBtn") function goBtn:tap (event) composer.gotoScene( "scene2")--, {effect = "fade", time = 300}) return true end goBtn:addEventListener("tap", goBtn) elseif phase == "did" then local function onComplete( event ) if event.completed == false then event.completed = true print(event.completed) end end -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc -- we obtain the object by id from the scene's object hierarchy if logo.new == false then selectNewLogo = logo sceneGroup:insert(selectNewLogo) if selectNewLogo then function selectNewLogo:touch (event) local phase = event.phase if phase == "ended" then if media.hasSource( media.PhotoLibrary ) then media.selectPhoto( { mediaSource=media.PhotoLibrary, destination = { baseDir= system.DocumentsDirectory, filename= "userLogo.jpg", type = "image"}, listener=onComplete } ) else native.showAlert( "Corona", "This device does not have a photo library.", { "OK" } ) end end --composer.gotoScene( "scene1")--,{effect = "fade", time = 300}) end selectNewLogo:addEventListener("touch", selectNewLogo) end end end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if event.phase == "will" then print("will") --logo.isVisible = false if selectNewLogo then selectNewLogo:removeEventListener("touch", selectNewLogo) end -- Called when the scene is on screen and is about to move off screen -- -- INSERT code here to pause the scene -- e.g. stop timers, stop animation, unload sounds, etc.) elseif phase == "did" then -- Called when the scene is now off screen -- if nextSceneButton then -- nextSceneButton:removeEventListener( "touch", nextSceneButton ) -- end end end function scene:destroy( event ) local sceneGroup = self.view -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

--------------------------------------------------------------------------------- -- -- scene3.lua -- --------------------------------------------------------------------------------- local sceneName = ... local sqlite3 = require( "sqlite3" ) local json = require("json") local composer = require( "composer" ) -- Load scene with same root filename as this file local scene = composer.newScene( sceneName ) --------------------------------------------------------------------------------- local nextSceneButton function scene:create( event ) local sceneGroup = self.view -- Called when the scene's view does not exist -- -- INSERT code here to initialize the scene -- e.g. add display objects to 'sceneGroup', add touch listeners, etc end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is still off screen and is about to move on screen t = {} local goToScene1Btn = self:getObjectByName( "GoToScene1Btn" ) goToScene1Btn.x = display.contentWidth - 95 goToScene1Btn.y = display.contentHeight - 35 local goToScene1Text = self:getObjectByName( "GoToScene1Text" ) goToScene1Text.x = display.contentWidth - 92 goToScene1Text.y = display.contentHeight - 35 local db = sqlite3.open\_memory() db:exec[[CREATE TABLE test (id INTEGER PRIMARY KEY, content);]] print( "version " .. sqlite3.version() ) for row in db:nrows("SELECT \* FROM test") do t[row.id] = display.newText( row.content, display.contentWidth/2, 30\*row.id, nil, 16 ) t[row.id]:setFillColor( 1, 0, 1 ) sceneGroup:insert(t[row.id]) end -- text field for task name local taskName = display.newText("Task Name",display.contentWidth/2, (display.contentHeight/2)-50, native.systemFont ) taskName:setFillColor{0,0,0} local fieldBG = display.newRect(display.contentWidth/2, display.contentHeight/2 , display.contentWidth-96, 20) local paint = { 0, 0, 0 } fieldBG.fill = paint titleField = native.newTextField( display.contentWidth/2, display.contentHeight/2 , display.contentWidth-100, 16) --text field for task details local taskDetails = display.newText("Task Details",display.contentWidth/2, (display.contentHeight/2)+50, native.systemFont ) taskDetails:setFillColor{0,0,0} local fieldBG2 = display.newRect(display.contentWidth/2, (display.contentHeight/2) + 100, display.contentWidth-96, 20) fieldBG2.fill = paint detailField = native.newTextField( display.contentWidth/2, (display.contentHeight/2) + 100, display.contentWidth-100, 16) --task submission button taskSubmitBtn = display.newRect(display.contentWidth/2, (display.contentHeight/2) + 150, display.contentWidth/4, 40) sceneGroup:insert(taskSubmitBtn) local paint = { 0, 0, 0 } taskSubmitBtn.fill = paint local SubmitBtnText = display.newText("Add Task",display.contentWidth/2, (display.contentHeight/2)+ 150, native.systemFont ) taskDetails:setFillColor{0,0,0} function taskSubmitBtn:touch (event) if event.phase == "ended" then taskTable = {} taskTable[#taskTable+1] = { taskTitle = titleField.text, taskDetails = detailField.text } print(taskTable[#taskTable].taskTitle) print(taskTable[#taskTable].taskDetails) end end taskSubmitBtn:addEventListener("touch", taskSubmitBtn) sceneGroup:insert(taskName) sceneGroup:insert(fieldBG) sceneGroup:insert(taskDetails) sceneGroup:insert(fieldBG2) sceneGroup:insert(taskSubmitBtn) sceneGroup:insert(SubmitBtnText) elseif phase == "did" then -- print("did") -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc nextSceneButton = self:getObjectByName( "GoToScene1Btn" ) if nextSceneButton then -- touch listener for the button function nextSceneButton:touch ( event ) if "ended" == event.phase then composer.gotoScene( "scene1")--, { effect = "fade", time = 300 } ) return true end end -- add the touch event listener to the button nextSceneButton:addEventListener( "touch", nextSceneButton ) end end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if event.phase == "will" then if titleField then titleField.isVisible = false -- titleField:removeSelf() --titleField = nil end if detailField then detailField.isVisible = false --detailField:removeSelf() --detailField = nil end -- titleField.isVisible = false -- detailField.isVisible = false --detailField:removeSelf() -- for i=1 , #t, 1 do -- t[i].isVisible = false -- end -- Called when the scene is on screen and is about to move off screen -- -- INSERT code here to pause the scene -- e.g. stop timers, stop animation, unload sounds, etc.) elseif phase == "did" then -- Called when the scene is now off screen --if nextSceneButton then taskSubmitBtn:removeEventListener("touch", taskSubmitBtn) nextSceneButton:removeEventListener( "touch", nextSceneButton ) --end end end function scene:destroy( event ) local sceneGroup = self.view -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

--------------------------------------------------------------------------------- -- -- scene2.lua -- --------------------------------------------------------------------------------- local sceneName = ... local composer = require( "composer" ) -- Load scene with same root filename as this file local scene = composer.newScene( sceneName ) --------------------------------------------------------------------------------- local nextSceneButton function scene:create( event ) local sceneGroup = self.view -- Called when the scene's view does not exist -- -- INSERT code here to initialize the scene -- e.g. add display objects to 'sceneGroup', add touch listeners, etc end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is still off screen and is about to move on screen local title = self:getObjectByName( "Title" ) title.x = display.contentWidth / 2 title.y = display.contentHeight / 2 title.size = display.contentWidth / 10 local goToScene3Btn = self:getObjectByName( "GoToScene3Btn" ) goToScene3Btn.x = display.contentWidth - 95 goToScene3Btn.y = display.contentHeight - 35 local goToScene3Text = self:getObjectByName( "GoToScene3Text" ) goToScene3Text.x = display.contentWidth - 92 goToScene3Text.y = display.contentHeight - 35 nextSceneButton = self:getObjectByName( "GoToScene3Btn" ) elseif phase == "did" then -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc if nextSceneButton then -- touch listener for the button function nextSceneButton:touch ( event ) local phase = event.phase if phase == "ended" then composer.gotoScene( "scene3")--, { effect = "fade", time = 300 } ) print("go to 3") end return true end -- add the touch event listener to the button nextSceneButton:addEventListener( "touch", nextSceneButton ) end end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if event.phase == "will" then -- Called when the scene is on screen and is about to move off screen -- INSERT code here to pause the scene -- e.g. stop timers, stop animation, unload sounds, etc.) elseif phase == "did" then -- Called when the scene is now off screen if nextSceneButton then nextSceneButton:removeEventListener( "touch", nextSceneButton ) end end end function scene:destroy( event ) local sceneGroup = self.view -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

Thank you for the responses. I have looked at the event handler phases but didn’t recognize anything that may cause a problem. The issue arises after I have looped through the scenes 3 times. As you can see, most of this is just cut and pasted sample code. Sorry for posting so much erroneous code. I am just not sure where the problem is. 

Michael,

I am guessing you are using the Composer GUI, which I have never used.  I use the Composer Library.  I am not familiar with the function ‘getObjectByName()’  but in any case the essence of the problem is still the same.

Looking at the code just briefly, I see in all 3 scenes you declare a nextSceneButton,  but in scene1 you do not define it anywhere … and instead I am guessing you have the ‘goBtn’ you are using to change scene instead of the nextSceneButton.   

I would first declare goBtn ‘local’ at the top of the scene where you have  nextSceneButton declared.   (actually, I would try to be consistent in the 3 scene modules by using nextSceneButton in each … as they each ‘appear’ to do the same basic thing in each of the scenes… that is they each change scene)…  but as you already have it named ‘goBtn’, the quick fix here is just this :

  1. define goBtn ‘local’ at top of the file

  2. be sure to ‘remove’ it’s event listener on the scene:hide   … like you did in the other 2 modules for the nextSceneBtns

However, I have no knowledge on the GUI tool.  I have never used it so, I am not sure what things happen when you call that  GUI function ‘getObjectByName()’, or what code the GUI tool creates for you.  I am sorry I cannot be any help on that.

I also noticed you used ‘tap’ event for the goBtn and ‘touch’ events for the nextSceneButtons; which is fine… both work. The difference is mostly in what Rob mentioned…  the touch event will have at least 2 phases that will fire. It appears you understand the difference between the tap and the touch; if not it would be good to check the corona docs on those 2 events as they are so often used.

Hope this helps.

Good Luck

Bob

Wow! I can’t believe it was that simple. Removing the event listener seems to have fixed the problems I was having. Thank you! Now for a little clarification. Using composer, when I create an object, add it to the scene.group and then add an event listener to that object composer will manage the object on the display but I must manually manage ALL of my listeners? 

Michael,

My understanding Composer will  manage only the  Corona ‘Display Objects’, if added to the scene group, should be handled by composer when the scene is  ‘destroyed’  which would take care of removing the event listener on those corona display objects as well.

Here is list of display objects : https://docs.coronalabs.com/daily/api/type/DisplayObject/index.html

However, when the scene only hides, those are not removed.  So if you are going to add event listeners to a display object in the scene:show function you need to remove those event listeners yourself in the scene:hide function. Think of the scene:show and the scene:hide as working in tandem.  If you do no remove them in scene:hide,  each scene:show creates a new event listener on that display object.  

But, to confirm that I understand it and explained it correctly, you should check out this tutorial, forum post, and these docs. They may explain it better then I.  

https://coronalabs.com/blog/2014/06/03/tutorial-understanding-the-composer-api/

https://forums.coronalabs.com/topic/51832-does-composer-automatically-handle-removal-of-display-objects-timers-sounds-eventlisteners-when-scenehide-is-called/

https://docs.coronalabs.com/daily/guide/events/detectEvents/index.html

https://docs.coronalabs.com/daily/guide/system/composer/index.html

Good luck.  

Bob

Could you post your code for the scene that is calling the gotoScene() multiple times?

Use the ‘<>’ icon on the toolbar to paste your code in the correct format.  If you just paste your code into the reply screen directly it will not format well and be hard to read.

My first guess from the little info provided is you have an eventListener that is not getting removed when you leave a scene and each return to that scene creates an additional eventListener that is calling the ‘gotoScene()’ …  but I am just guessing.  If you can post the code someone on the forum will likely figure it out quickly.

Bob

Look for button event handlers where you might not be handling both the “began” and “ended” phases? Look for collisions that could be triggering multiple times.

Rob

--------------------------------------------------------------------------------- -- -- scene1.lua -- --------------------------------------------------------------------------------- local sceneName = ... local composer = require( "composer" ) -- Load scene with same root filename as this file local scene = composer.newScene( sceneName ) --------------------------------------------------------------------------------- local nextSceneButton local selectNewLogo local logo --local userLogoExists = false --local logoGroup function scene:create( event ) local sceneGroup = self.view -- Called when the scene's view does not exist -- -- INSERT code here to initialize the scene -- e.g. add display objects to 'sceneGroup', add touch listeners, etc end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then trumpFace = self:getObjectByName("trump") trumpFace.isVisible = false -- Called when the scene is still off screen and is about to move on screen local logoPath = system.pathForFile( "userLogo.jpg", system.DocumentsDirectory ) local logoFile = io.open(logoPath) if logoFile then --trumpFace.isVisible = false logo = nil logo = display.newImage("userLogo.jpg", system.DocumentsDirectory) io.close(logoFile) logoFile = nil logo:scale(.15,.15) logo.x = display.contentWidth/2 logo.y = display.contentHeight/2 logo.new = true else trumpFace.isVisible = true logo = trumpFace logo.new = false end sceneGroup:insert(logo) goBtn = self:getObjectByName("goBtn") function goBtn:tap (event) composer.gotoScene( "scene2")--, {effect = "fade", time = 300}) return true end goBtn:addEventListener("tap", goBtn) elseif phase == "did" then local function onComplete( event ) if event.completed == false then event.completed = true print(event.completed) end end -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc -- we obtain the object by id from the scene's object hierarchy if logo.new == false then selectNewLogo = logo sceneGroup:insert(selectNewLogo) if selectNewLogo then function selectNewLogo:touch (event) local phase = event.phase if phase == "ended" then if media.hasSource( media.PhotoLibrary ) then media.selectPhoto( { mediaSource=media.PhotoLibrary, destination = { baseDir= system.DocumentsDirectory, filename= "userLogo.jpg", type = "image"}, listener=onComplete } ) else native.showAlert( "Corona", "This device does not have a photo library.", { "OK" } ) end end --composer.gotoScene( "scene1")--,{effect = "fade", time = 300}) end selectNewLogo:addEventListener("touch", selectNewLogo) end end end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if event.phase == "will" then print("will") --logo.isVisible = false if selectNewLogo then selectNewLogo:removeEventListener("touch", selectNewLogo) end -- Called when the scene is on screen and is about to move off screen -- -- INSERT code here to pause the scene -- e.g. stop timers, stop animation, unload sounds, etc.) elseif phase == "did" then -- Called when the scene is now off screen -- if nextSceneButton then -- nextSceneButton:removeEventListener( "touch", nextSceneButton ) -- end end end function scene:destroy( event ) local sceneGroup = self.view -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

--------------------------------------------------------------------------------- -- -- scene3.lua -- --------------------------------------------------------------------------------- local sceneName = ... local sqlite3 = require( "sqlite3" ) local json = require("json") local composer = require( "composer" ) -- Load scene with same root filename as this file local scene = composer.newScene( sceneName ) --------------------------------------------------------------------------------- local nextSceneButton function scene:create( event ) local sceneGroup = self.view -- Called when the scene's view does not exist -- -- INSERT code here to initialize the scene -- e.g. add display objects to 'sceneGroup', add touch listeners, etc end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is still off screen and is about to move on screen t = {} local goToScene1Btn = self:getObjectByName( "GoToScene1Btn" ) goToScene1Btn.x = display.contentWidth - 95 goToScene1Btn.y = display.contentHeight - 35 local goToScene1Text = self:getObjectByName( "GoToScene1Text" ) goToScene1Text.x = display.contentWidth - 92 goToScene1Text.y = display.contentHeight - 35 local db = sqlite3.open\_memory() db:exec[[CREATE TABLE test (id INTEGER PRIMARY KEY, content);]] print( "version " .. sqlite3.version() ) for row in db:nrows("SELECT \* FROM test") do t[row.id] = display.newText( row.content, display.contentWidth/2, 30\*row.id, nil, 16 ) t[row.id]:setFillColor( 1, 0, 1 ) sceneGroup:insert(t[row.id]) end -- text field for task name local taskName = display.newText("Task Name",display.contentWidth/2, (display.contentHeight/2)-50, native.systemFont ) taskName:setFillColor{0,0,0} local fieldBG = display.newRect(display.contentWidth/2, display.contentHeight/2 , display.contentWidth-96, 20) local paint = { 0, 0, 0 } fieldBG.fill = paint titleField = native.newTextField( display.contentWidth/2, display.contentHeight/2 , display.contentWidth-100, 16) --text field for task details local taskDetails = display.newText("Task Details",display.contentWidth/2, (display.contentHeight/2)+50, native.systemFont ) taskDetails:setFillColor{0,0,0} local fieldBG2 = display.newRect(display.contentWidth/2, (display.contentHeight/2) + 100, display.contentWidth-96, 20) fieldBG2.fill = paint detailField = native.newTextField( display.contentWidth/2, (display.contentHeight/2) + 100, display.contentWidth-100, 16) --task submission button taskSubmitBtn = display.newRect(display.contentWidth/2, (display.contentHeight/2) + 150, display.contentWidth/4, 40) sceneGroup:insert(taskSubmitBtn) local paint = { 0, 0, 0 } taskSubmitBtn.fill = paint local SubmitBtnText = display.newText("Add Task",display.contentWidth/2, (display.contentHeight/2)+ 150, native.systemFont ) taskDetails:setFillColor{0,0,0} function taskSubmitBtn:touch (event) if event.phase == "ended" then taskTable = {} taskTable[#taskTable+1] = { taskTitle = titleField.text, taskDetails = detailField.text } print(taskTable[#taskTable].taskTitle) print(taskTable[#taskTable].taskDetails) end end taskSubmitBtn:addEventListener("touch", taskSubmitBtn) sceneGroup:insert(taskName) sceneGroup:insert(fieldBG) sceneGroup:insert(taskDetails) sceneGroup:insert(fieldBG2) sceneGroup:insert(taskSubmitBtn) sceneGroup:insert(SubmitBtnText) elseif phase == "did" then -- print("did") -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc nextSceneButton = self:getObjectByName( "GoToScene1Btn" ) if nextSceneButton then -- touch listener for the button function nextSceneButton:touch ( event ) if "ended" == event.phase then composer.gotoScene( "scene1")--, { effect = "fade", time = 300 } ) return true end end -- add the touch event listener to the button nextSceneButton:addEventListener( "touch", nextSceneButton ) end end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if event.phase == "will" then if titleField then titleField.isVisible = false -- titleField:removeSelf() --titleField = nil end if detailField then detailField.isVisible = false --detailField:removeSelf() --detailField = nil end -- titleField.isVisible = false -- detailField.isVisible = false --detailField:removeSelf() -- for i=1 , #t, 1 do -- t[i].isVisible = false -- end -- Called when the scene is on screen and is about to move off screen -- -- INSERT code here to pause the scene -- e.g. stop timers, stop animation, unload sounds, etc.) elseif phase == "did" then -- Called when the scene is now off screen --if nextSceneButton then taskSubmitBtn:removeEventListener("touch", taskSubmitBtn) nextSceneButton:removeEventListener( "touch", nextSceneButton ) --end end end function scene:destroy( event ) local sceneGroup = self.view -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

--------------------------------------------------------------------------------- -- -- scene2.lua -- --------------------------------------------------------------------------------- local sceneName = ... local composer = require( "composer" ) -- Load scene with same root filename as this file local scene = composer.newScene( sceneName ) --------------------------------------------------------------------------------- local nextSceneButton function scene:create( event ) local sceneGroup = self.view -- Called when the scene's view does not exist -- -- INSERT code here to initialize the scene -- e.g. add display objects to 'sceneGroup', add touch listeners, etc end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is still off screen and is about to move on screen local title = self:getObjectByName( "Title" ) title.x = display.contentWidth / 2 title.y = display.contentHeight / 2 title.size = display.contentWidth / 10 local goToScene3Btn = self:getObjectByName( "GoToScene3Btn" ) goToScene3Btn.x = display.contentWidth - 95 goToScene3Btn.y = display.contentHeight - 35 local goToScene3Text = self:getObjectByName( "GoToScene3Text" ) goToScene3Text.x = display.contentWidth - 92 goToScene3Text.y = display.contentHeight - 35 nextSceneButton = self:getObjectByName( "GoToScene3Btn" ) elseif phase == "did" then -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc if nextSceneButton then -- touch listener for the button function nextSceneButton:touch ( event ) local phase = event.phase if phase == "ended" then composer.gotoScene( "scene3")--, { effect = "fade", time = 300 } ) print("go to 3") end return true end -- add the touch event listener to the button nextSceneButton:addEventListener( "touch", nextSceneButton ) end end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if event.phase == "will" then -- Called when the scene is on screen and is about to move off screen -- INSERT code here to pause the scene -- e.g. stop timers, stop animation, unload sounds, etc.) elseif phase == "did" then -- Called when the scene is now off screen if nextSceneButton then nextSceneButton:removeEventListener( "touch", nextSceneButton ) end end end function scene:destroy( event ) local sceneGroup = self.view -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

Thank you for the responses. I have looked at the event handler phases but didn’t recognize anything that may cause a problem. The issue arises after I have looped through the scenes 3 times. As you can see, most of this is just cut and pasted sample code. Sorry for posting so much erroneous code. I am just not sure where the problem is. 

Michael,

I am guessing you are using the Composer GUI, which I have never used.  I use the Composer Library.  I am not familiar with the function ‘getObjectByName()’  but in any case the essence of the problem is still the same.

Looking at the code just briefly, I see in all 3 scenes you declare a nextSceneButton,  but in scene1 you do not define it anywhere … and instead I am guessing you have the ‘goBtn’ you are using to change scene instead of the nextSceneButton.   

I would first declare goBtn ‘local’ at the top of the scene where you have  nextSceneButton declared.   (actually, I would try to be consistent in the 3 scene modules by using nextSceneButton in each … as they each ‘appear’ to do the same basic thing in each of the scenes… that is they each change scene)…  but as you already have it named ‘goBtn’, the quick fix here is just this :

  1. define goBtn ‘local’ at top of the file

  2. be sure to ‘remove’ it’s event listener on the scene:hide   … like you did in the other 2 modules for the nextSceneBtns

However, I have no knowledge on the GUI tool.  I have never used it so, I am not sure what things happen when you call that  GUI function ‘getObjectByName()’, or what code the GUI tool creates for you.  I am sorry I cannot be any help on that.

I also noticed you used ‘tap’ event for the goBtn and ‘touch’ events for the nextSceneButtons; which is fine… both work. The difference is mostly in what Rob mentioned…  the touch event will have at least 2 phases that will fire. It appears you understand the difference between the tap and the touch; if not it would be good to check the corona docs on those 2 events as they are so often used.

Hope this helps.

Good Luck

Bob

Wow! I can’t believe it was that simple. Removing the event listener seems to have fixed the problems I was having. Thank you! Now for a little clarification. Using composer, when I create an object, add it to the scene.group and then add an event listener to that object composer will manage the object on the display but I must manually manage ALL of my listeners? 

Michael,

My understanding Composer will  manage only the  Corona ‘Display Objects’, if added to the scene group, should be handled by composer when the scene is  ‘destroyed’  which would take care of removing the event listener on those corona display objects as well.

Here is list of display objects : https://docs.coronalabs.com/daily/api/type/DisplayObject/index.html

However, when the scene only hides, those are not removed.  So if you are going to add event listeners to a display object in the scene:show function you need to remove those event listeners yourself in the scene:hide function. Think of the scene:show and the scene:hide as working in tandem.  If you do no remove them in scene:hide,  each scene:show creates a new event listener on that display object.  

But, to confirm that I understand it and explained it correctly, you should check out this tutorial, forum post, and these docs. They may explain it better then I.  

https://coronalabs.com/blog/2014/06/03/tutorial-understanding-the-composer-api/

https://forums.coronalabs.com/topic/51832-does-composer-automatically-handle-removal-of-display-objects-timers-sounds-eventlisteners-when-scenehide-is-called/

https://docs.coronalabs.com/daily/guide/events/detectEvents/index.html

https://docs.coronalabs.com/daily/guide/system/composer/index.html

Good luck.  

Bob