Understanding Modular coding (creating a modular tabBar

Hi All,

I am new to Corona SDK and to Lua scripting but have done my basic reading and developed some scenes to create a prototype of the app. Now time to understand optimisation and modular coding. 

Essentially, I have a common tabBar across most of the pages and therefore would like to modularise thecode to be able to create a tabbar along with it’s buttons and handler events in a common module and be able to call this from various scenes. I know this should be pretty straight forward but I am just not sure what am I missing. 

Can the experts please help. Following is the simplified code:

– main.lua

local composer = require ( “composer” )

local scene = composer.newScene()

local widget = require( “widget” )

function scene:create(event)

    local loadCommonMod = require “comModule”

    local catTabBar = loadCommonMod.initateNavBar()

    sceneGroup:insert( catTabBar )

end

– comModule.lua

t={}


– initiateNavBar 


function initiateNavBar ()

   function displayNavBar(){

            {

                width = 72, 

                height = 60,

                defaultFile = “images/refresh.png”,

                label = “Start Over”,

                size = 16,

                labelYOffset = -8,

                onPress = handleStartOverEvent     

            },

            {

                width = 72, 

                height = 60,

                defaultFile = “images/basket.png”,

                label = “My Basket”,

                size = 16,

                labelYOffset = -8,

                onPress = handleMyBasketEvent     

            }

        }

        – -- Create the widget

        local tabBar = widget.newTabBar

         {

             left = 0,

             top = 0,

             width = display.contentWidth,

             height = 150,

             buttons = tabButtons   

         }

    end

   displayNavBar()

    --tabBar = display.newText(“Test”,0,0,global.font,20)

    --tabBar:setFillColor( 0,0,1 )

    return tabBar

end 

t.initateNavBar=initiateNavBar()


– initiateNavBar 


return t  

Any help will be much appreciated,

Thanks

The code you posted would not compile. You start the displayNavBar function with declaring a table but you don’t assign it to any variable. Second problem is that tabBar is local to the displayNavBar function thus return tabBar which is outside this function will actualy return nil. Try this in comModule.lua (not tested)

Note: You still need to define the handleStartOverEvent and handleMyBasketEvent

t={} ------------------------------------------- -- initiateNavBar ------------------------------------------- function initiateNavBar () function displayNavBar() local tabButtons = { { width = 72, height = 60, defaultFile = "images/refresh.png", label = "Start Over", size = 16, labelYOffset = -8, onPress = handleStartOverEvent }, { width = 72, height = 60, defaultFile = "images/basket.png", label = "My Basket", size = 16, labelYOffset = -8, onPress = handleMyBasketEvent } } -- -- Create the widget local tabBar = widget.newTabBar { left = 0, top = 0, width = display.contentWidth, height = 150, buttons = tabButtons } return tabBar end return displayNavBar() end t.initateNavBar=initiateNavBar() ------------------------------------------- -- initiateNavBar ------------------------------------------- return t v

If you have this nav bar in all your scenes than you can actualy leave it outside of any scene and it will persist over all your scenes. I think that would be a better way if you don’t need to change it in every scene.

Thanks for your prompt reply primoz.ceror. Apologies, just to clarify the nav bar will be visible always apart from the splash screen and the login screen. I am not sure if what you’ve mentioned will still hold true as the user may navigate back to login screen (for whatever reasons). But I agree if this is achievable, I would love to persist a nav bar loaded once.

Also, I tried the suggestion you mentioned earlier, but I get the an error “attempt to call field initiateNavBar (a table value)” with the following code I have in one o my scenes:

    local loadCommonMod = require “comModule”

    local catTabBar = loadCommonMod.initiateNavBar()

    sceneGroup:insert( catTabBar )

Can you please help me with what is wrong.

Thanks again.

Well you can just set navBar.isVisible = false in splash and login screen and then make it visible again when you need it.

Remove the brackets in this line:

t.init i ateNavBar=initiateNavBar() --also you have a typo here there is an i missing.

change to

t.initiateNavBar=initiateNavBar

Thanks.

When you say “If you have this nav bar in all your scenes than you can actualy leave it outside of any scene and it will persist over all your scenes”, where is the best place I should initiate the nav bar for the first time (main.lua or splash screen). And will it persist even after a composer.gotoScene transition to other screen?

I would show it in main.lua afterthe splash screen so you don’t have to worry about it appearing on the splash screen. Yes it will persist. Just make sure you don’t  place something over it in one of your scenes.

Perfect, thanks your insight and help is really appreciated - this is what I will do.

Slightly different question though on the same topic with regards to understanding defining variables and methods in common modules. Apologies for very fundamental questions but I somehow manage to get things to work without completely understanding how it did work and struggle to replicate. I have tried to go through some tutorials but still not getting my heads around it. Can you please answer the following scenario assuming global.lua is my common module (apologies my background is pl/sql, unix shell scripting and hence trying to relate):

– global.lua

t = {}

font = native.systemFont

fontBold = native.systemFontBold

statusBarH = display.topStatusBarContentHeight

tabBarH = 120

isAndroid = “Android” == system.getInfo(“platformName”)

function func1()

end

function func3()

   function meth1()

      local funcTxt = display.newText(“Label”,10,10,global.font)

      …

      return funcTxt

   end

end

return t


– main.lua

local global = require(“global”)

– How can I

– Quest 1. access variables from global.lua, for eg: 

local txt = display.newText(“Label”,10,10,global.font)

– Quest 2. modify values of these global variables such that the modified values persist through out the application, for eg:

if global.isAndroid == “Android” then

global.tabBarH= 150 

end

– Quest 3. Call func1() which completes certain activities

– Quest 4. Call func3.meth1()  which returns a Text object

local test = func3.meth1

test:setFillColor(1,0,0)


– scene1.lua

local global = require(“global”)

print(global.tabBarH) – results in 150


Hopefully once I understand the above concepts, I will be able to modularise my code.

Thanks

ok, I just read the following page carefully which answered first 3 questions and I have got it working:

http://coronalabs.com/blog/2012/08/28/how-external-modules-work-in-corona/

I will try and see if I can get the scenario in question 4 working as well. 

Thanks,

There are some issues here. First you are defining everything global which is a bad thing and can eventually slow down and/or break you code. Make sure you use the word local in front of everything. If you want to able to access the values outside the module then set them on the module table like so:

t = {}

t.font = native.systemFont
t.fontBold = native.systemFontBold
t.statusBarH = display.topStatusBarContentHeight
t.tabBarH = 120
t.isAndroid = “Android” == system.getInfo(“platformName”)

local function func1()

end

t.func1 = func1

You can’t get 4 to work because you defined the meth1 inside the func3. If you need to call meth1 outside the func3 then define it separately in add a reference to it in the module table like func1 above.

Then when you need anything from the global.lua after you require it with local global = require(“global”) access the values with global.font and functions with global.func1().

Excellent, thanks. Very well explained and it is much clearer to me and my code is like the way i wanted. 

I have read elsewhere in the forum and from what I understand this functionality is dropped now but just to get a confirmation can I ask if I can unslelect a button from tabBar. For eg: In the common nav bar I have a “back” button which works fine i.e. when touched takes me to the previous/parent screen but the button continues to be selected. So if the user selects this back button and is taken to the previous screen the user cannot tap on the same back button again as it is already selected and therefore the only option is to select some other button (which unslelects back button) and then tap on back button.

I have tried comObj.tabBar:setSelected(0, false) or try to set selection to something else but it only simulates the touch event for other button and the back button continues to be selected.

Ideally there should be a way to unselect all the buttons but if that is not possible, can suggest a practical work around please.

Thanks,

That is because the tab bar is not meant for this kind of use. It is meant to select the scene or view where one is always active thus it remains pressed. If you need buttons that perform a specific function and return to unpressed state then I would recommend creating a group with normal buttons (widget.newButton).

Thank you very much, makes sense. I have resorted to Buttons and everything is working fine. 

The code you posted would not compile. You start the displayNavBar function with declaring a table but you don’t assign it to any variable. Second problem is that tabBar is local to the displayNavBar function thus return tabBar which is outside this function will actualy return nil. Try this in comModule.lua (not tested)

Note: You still need to define the handleStartOverEvent and handleMyBasketEvent

t={} ------------------------------------------- -- initiateNavBar ------------------------------------------- function initiateNavBar () function displayNavBar() local tabButtons = { { width = 72, height = 60, defaultFile = "images/refresh.png", label = "Start Over", size = 16, labelYOffset = -8, onPress = handleStartOverEvent }, { width = 72, height = 60, defaultFile = "images/basket.png", label = "My Basket", size = 16, labelYOffset = -8, onPress = handleMyBasketEvent } } -- -- Create the widget local tabBar = widget.newTabBar { left = 0, top = 0, width = display.contentWidth, height = 150, buttons = tabButtons } return tabBar end return displayNavBar() end t.initateNavBar=initiateNavBar() ------------------------------------------- -- initiateNavBar ------------------------------------------- return t v

If you have this nav bar in all your scenes than you can actualy leave it outside of any scene and it will persist over all your scenes. I think that would be a better way if you don’t need to change it in every scene.

Thanks for your prompt reply primoz.ceror. Apologies, just to clarify the nav bar will be visible always apart from the splash screen and the login screen. I am not sure if what you’ve mentioned will still hold true as the user may navigate back to login screen (for whatever reasons). But I agree if this is achievable, I would love to persist a nav bar loaded once.

Also, I tried the suggestion you mentioned earlier, but I get the an error “attempt to call field initiateNavBar (a table value)” with the following code I have in one o my scenes:

    local loadCommonMod = require “comModule”

    local catTabBar = loadCommonMod.initiateNavBar()

    sceneGroup:insert( catTabBar )

Can you please help me with what is wrong.

Thanks again.

Well you can just set navBar.isVisible = false in splash and login screen and then make it visible again when you need it.

Remove the brackets in this line:

t.init i ateNavBar=initiateNavBar() --also you have a typo here there is an i missing.

change to

t.initiateNavBar=initiateNavBar

Thanks.

When you say “If you have this nav bar in all your scenes than you can actualy leave it outside of any scene and it will persist over all your scenes”, where is the best place I should initiate the nav bar for the first time (main.lua or splash screen). And will it persist even after a composer.gotoScene transition to other screen?

I would show it in main.lua afterthe splash screen so you don’t have to worry about it appearing on the splash screen. Yes it will persist. Just make sure you don’t  place something over it in one of your scenes.

Perfect, thanks your insight and help is really appreciated - this is what I will do.

Slightly different question though on the same topic with regards to understanding defining variables and methods in common modules. Apologies for very fundamental questions but I somehow manage to get things to work without completely understanding how it did work and struggle to replicate. I have tried to go through some tutorials but still not getting my heads around it. Can you please answer the following scenario assuming global.lua is my common module (apologies my background is pl/sql, unix shell scripting and hence trying to relate):

– global.lua

t = {}

font = native.systemFont

fontBold = native.systemFontBold

statusBarH = display.topStatusBarContentHeight

tabBarH = 120

isAndroid = “Android” == system.getInfo(“platformName”)

function func1()

end

function func3()

   function meth1()

      local funcTxt = display.newText(“Label”,10,10,global.font)

      …

      return funcTxt

   end

end

return t


– main.lua

local global = require(“global”)

– How can I

– Quest 1. access variables from global.lua, for eg: 

local txt = display.newText(“Label”,10,10,global.font)

– Quest 2. modify values of these global variables such that the modified values persist through out the application, for eg:

if global.isAndroid == “Android” then

global.tabBarH= 150 

end

– Quest 3. Call func1() which completes certain activities

– Quest 4. Call func3.meth1()  which returns a Text object

local test = func3.meth1

test:setFillColor(1,0,0)


– scene1.lua

local global = require(“global”)

print(global.tabBarH) – results in 150


Hopefully once I understand the above concepts, I will be able to modularise my code.

Thanks