Button event not working properly when compiled. Sample code included

I have a problem with an application. I’ve created an over simplified piece of code that shows this.
The following code runs fine in Corona but not on an iOS device.

Run the application in Corona. Press “Button One”. Acknowledge the alert. Press “Button Two”, nothing happens - correct behavior.

Compile and run in either the iOS simulator or an iOS device. Press “Button One”. Acknowledge the alert. Press “Button Two”, now the alert from Button One is displayed again.

Very simple code example:

[code]


– main.lua


– Your code here
local ui = require(“ui”)

local PerformSomeAction = function (event)
–do nothing
end

local onButtonEvent = function (event )
if event.id == “One” then
alert = native.showAlert( “Button One Pressed”, “Try Button Two Next”, { “YES”, “NO” }, PerformSomeAction() )
end
if event.id == “Two” then
–do nothing
end
return true
end

local myFirstButton = ui.newButton{
default = “buttonBlue.png”,
over = “buttonBlueOver.png”,
onEvent = onButtonEvent,
id = “One”,
text = “Button One”,
font = “Trebuchet-BoldItalic”,
textColor = { 255, 255, 255, 255 },
size = 12,
emboss = true
}

myFirstButton.x = display.contentCenterX
myFirstButton.y = 150
local mySecondButton = ui.newButton{
default = “buttonBlue.png”,
over = “buttonBlueOver.png”,
onEvent = onButtonEvent,
id = “Two”,
text = “Button Two”,
font = “Trebuchet-BoldItalic”,
textColor = { 255, 255, 255, 255 },
size = 12,
emboss = true
}

mySecondButton.x = display.contentCenterX
mySecondButton.y = 300
[/code] [import]uid: 74957 topic_id: 31827 reply_id: 331827[/import]

ui.lua

[code]
– ui.lua (currently includes Button class with labels, font selection and optional event model)

– Version 1.4 (works with multitouch)

module(…, package.seeall)


– Helper function for newButton utility function below
local function newButtonHandler( self, event )

local result = true

local default = self[1]
local over = self[2]

– General “onEvent” function overrides onPress and onRelease, if present
local onEvent = self._onEvent

local onPress = self._onPress
local onRelease = self._onRelease

local buttonEvent = {}
if (self._id) then
buttonEvent.id = self._id
end

local phase = event.phase
if “began” == phase then
if over then
default.isVisible = false
over.isVisible = true
end

if onEvent then
buttonEvent.phase = “press”
result = onEvent( buttonEvent )
elseif onPress then
result = onPress( event )
end

– Subsequent touch events will target button even if they are outside the stageBounds of button
display.getCurrentStage():setFocus( self, event.id )
self.isFocus = true

elseif self.isFocus then
local bounds = self.stageBounds
local x,y = event.x,event.y
local isWithinBounds =
bounds.xMin <= x and bounds.xMax >= x and bounds.yMin <= y and bounds.yMax >= y

if “moved” == phase then
if over then
– The rollover image should only be visible while the finger is within button’s stageBounds
default.isVisible = not isWithinBounds
over.isVisible = isWithinBounds
end

elseif “ended” == phase or “cancelled” == phase then
if over then
default.isVisible = true
over.isVisible = false
end

if “ended” == phase then
– Only consider this a “click” if the user lifts their finger inside button’s stageBounds
if isWithinBounds then
if onEvent then
buttonEvent.phase = “release”
result = onEvent( buttonEvent )
elseif onRelease then
result = onRelease( event )
end
end
end

– Allow touch events to be sent normally to the objects they “hit”
display.getCurrentStage():setFocus( self, nil )
self.isFocus = false
end
end

return result
end

– Button class

function newButton( params )
local button, default, over, size, font, textColor, offset

if params.default then
button = display.newGroup()
default = display.newImage( params.default )
button:insert( default, true )
end

if params.over then
over = display.newImage( params.over )
over.isVisible = false
button:insert( over, true )
end

if params.text then
if ( params.size and type(params.size) == “number” ) then size=params.size else size=20 end
if ( params.font ) then font=params.font else font=native.systemFontBold end
if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end

– Optional vertical correction for fonts with unusual baselines (I’m looking at you, Zapfino)
if ( params.offset and type(params.offset) == “number” ) then offset=params.offset else offset = 0 end

if ( params.emboss ) then
– Make the label text look “embossed” (also adjusts effect for textColor brightness)
local textBrightness = ( textColor[1] + textColor[2] + textColor[3] ) / 3

local labelHighlight = display.newText( params.text, 0, 0, font, size )
if ( textBrightness > 127) then
labelHighlight:setTextColor( 255, 255, 255, 20 )
else
labelHighlight:setTextColor( 255, 255, 255, 140 )
end
button:insert( labelHighlight, true )
labelHighlight.x = labelHighlight.x + 1.5; labelHighlight.y = labelHighlight.y + 1.5 + offset

local labelShadow = display.newText( params.text, 0, 0, font, size )
if ( textBrightness > 127) then
labelShadow:setTextColor( 0, 0, 0, 128 )
else
labelShadow:setTextColor( 0, 0, 0, 20 )
end
button:insert( labelShadow, true )
labelShadow.x = labelShadow.x - 1; labelShadow.y = labelShadow.y - 1 + offset
end

local labelText = display.newText( params.text, 0, 0, font, size )
labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )
button:insert( labelText, true )
labelText.y = labelText.y + offset
end

if ( params.onPress and ( type(params.onPress) == “function” ) ) then
button._onPress = params.onPress
end
if ( params.onRelease and ( type(params.onRelease) == “function” ) ) then
button._onRelease = params.onRelease
end

if (params.onEvent and ( type(params.onEvent) == “function” ) ) then
button._onEvent = params.onEvent
end

– Set button as a table listener by setting a table method and adding the button as its own table listener for “touch” events
button.touch = newButtonHandler
button:addEventListener( “touch”, button )

if params.x then
button.x = params.x
end

if params.y then
button.y = params.y
end

if params.id then
button._id = params.id
end

return button
end

– Label class

function newLabel( params )
local labelText
local size, font, textColor, align
local t = display.newGroup()

if ( params.bounds ) then
local bounds = params.bounds
local left = bounds[1]
local top = bounds[2]
local width = bounds[3]
local height = bounds[4]

if ( params.size and type(params.size) == “number” ) then size=params.size else size=20 end
if ( params.font ) then font=params.font else font=native.systemFontBold end
if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end
if ( params.offset and type(params.offset) == “number” ) then offset=params.offset else offset = 0 end
if ( params.align ) then align = params.align else align = “center” end

if ( params.text ) then
labelText = display.newText( params.text, 0, 0, font, size )
labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )
t:insert( labelText )
– TODO: handle no-initial-text case by creating a field with an empty string?

if ( align == “left” ) then
labelText.x = left + labelText.stageWidth * 0.5
elseif ( align == “right” ) then
labelText.x = (left + width) - labelText.stageWidth * 0.5
else
labelText.x = ((2 * left) + width) * 0.5
end
end

labelText.y = top + labelText.stageHeight * 0.5

– Public methods
function t:setText( newText )
if ( newText ) then
labelText.text = newText

if ( “left” == align ) then
labelText.x = left + labelText.stageWidth / 2
elseif ( “right” == align ) then
labelText.x = (left + width) - labelText.stageWidth / 2
else
labelText.x = ((2 * left) + width) / 2
end
end
end

function t:setTextColor( r, g, b, a )
local newR = 255
local newG = 255
local newB = 255
local newA = 255

if ( r and type® == “number” ) then newR = r end
if ( g and type(g) == “number” ) then newG = g end
if ( b and type(b) == “number” ) then newB = b end
if ( a and type(a) == “number” ) then newA = a end

labelText:setTextColor( r, g, b, a )
end
end

– Return instance (as display group)
return t

end
[/code] [import]uid: 74957 topic_id: 31827 reply_id: 127003[/import]

Ack!
http://developer.coronalabs.com/forum/2012/09/26/uilua-corona-simulator-919-and-922#comment-125398
Looks like ui is not supported. [import]uid: 74957 topic_id: 31827 reply_id: 127006[/import]

OK. Same behavior using widget.newButton

[code]


– main.lua


– Your code here
local widget = require “widget”

local PerformSomeAction = function (event)
–do nothing
end

local onButtonEvent = function (event )
if event.target.id == “One” then
alert = native.showAlert( “Button One Pressed”, “Try Button Two Next”, { “YES”, “NO” }, PerformSomeAction() )
end
if event.target.id == “Two” then
–do nothing
end
return true
end

local myFirstButton = widget.newButton{
id = “One”,
left = 100,
top = 200,
label = “Widget Button”,
width = 150, height = 28,
cornerRadius = 8,
onEvent = onButtonEvent
}
myFirstButton.x = display.contentCenterX
myFirstButton.y = 150
local mySecondButton = widget.newButton{
id = “Two”,
left = 100,
top = 200,
label = “Widget Button”,
width = 150, height = 28,
cornerRadius = 8,
onEvent = onButtonEvent
}

mySecondButton.x = display.contentCenterX
mySecondButton.y = 300
[/code] [import]uid: 74957 topic_id: 31827 reply_id: 127009[/import]

Try this:
[lua]local onButtonEvent = function (event )
if event.phase == “release” then
if event.target.id == “One” then
alert = native.showAlert( “Button One Pressed”, “Try Button Two Next”, { “YES”, “NO” }, PerformSomeAction() )
print “one”
elseif event.target.id == “Two” then
print “two”
end
end
end[/lua]

Without specifying the event phase, it will pass multiple requests to your function (press, moved, release) so by pressing it once it actually presses it twice (press & release). does that make sence?

Edit: You can use the above to get creative, maybe make some popup or animation if the button is pressed and then do your function once released. [import]uid: 62706 topic_id: 31827 reply_id: 127081[/import]

Thank you CraftyDeano. That works perfectly.
It makes complete sense, now that you have explained it, why it would work that way. Had the IDE handled my code the same way as my iOS device had, I might have figured this out for myself. The two different results threw me completely.

I really appreciate your help, it saved me a lot of time. [import]uid: 74957 topic_id: 31827 reply_id: 127091[/import]

No problem,

Glad it works and you understand it better :slight_smile: [import]uid: 62706 topic_id: 31827 reply_id: 127096[/import]

ui.lua

[code]
– ui.lua (currently includes Button class with labels, font selection and optional event model)

– Version 1.4 (works with multitouch)

module(…, package.seeall)


– Helper function for newButton utility function below
local function newButtonHandler( self, event )

local result = true

local default = self[1]
local over = self[2]

– General “onEvent” function overrides onPress and onRelease, if present
local onEvent = self._onEvent

local onPress = self._onPress
local onRelease = self._onRelease

local buttonEvent = {}
if (self._id) then
buttonEvent.id = self._id
end

local phase = event.phase
if “began” == phase then
if over then
default.isVisible = false
over.isVisible = true
end

if onEvent then
buttonEvent.phase = “press”
result = onEvent( buttonEvent )
elseif onPress then
result = onPress( event )
end

– Subsequent touch events will target button even if they are outside the stageBounds of button
display.getCurrentStage():setFocus( self, event.id )
self.isFocus = true

elseif self.isFocus then
local bounds = self.stageBounds
local x,y = event.x,event.y
local isWithinBounds =
bounds.xMin <= x and bounds.xMax >= x and bounds.yMin <= y and bounds.yMax >= y

if “moved” == phase then
if over then
– The rollover image should only be visible while the finger is within button’s stageBounds
default.isVisible = not isWithinBounds
over.isVisible = isWithinBounds
end

elseif “ended” == phase or “cancelled” == phase then
if over then
default.isVisible = true
over.isVisible = false
end

if “ended” == phase then
– Only consider this a “click” if the user lifts their finger inside button’s stageBounds
if isWithinBounds then
if onEvent then
buttonEvent.phase = “release”
result = onEvent( buttonEvent )
elseif onRelease then
result = onRelease( event )
end
end
end

– Allow touch events to be sent normally to the objects they “hit”
display.getCurrentStage():setFocus( self, nil )
self.isFocus = false
end
end

return result
end

– Button class

function newButton( params )
local button, default, over, size, font, textColor, offset

if params.default then
button = display.newGroup()
default = display.newImage( params.default )
button:insert( default, true )
end

if params.over then
over = display.newImage( params.over )
over.isVisible = false
button:insert( over, true )
end

if params.text then
if ( params.size and type(params.size) == “number” ) then size=params.size else size=20 end
if ( params.font ) then font=params.font else font=native.systemFontBold end
if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end

– Optional vertical correction for fonts with unusual baselines (I’m looking at you, Zapfino)
if ( params.offset and type(params.offset) == “number” ) then offset=params.offset else offset = 0 end

if ( params.emboss ) then
– Make the label text look “embossed” (also adjusts effect for textColor brightness)
local textBrightness = ( textColor[1] + textColor[2] + textColor[3] ) / 3

local labelHighlight = display.newText( params.text, 0, 0, font, size )
if ( textBrightness > 127) then
labelHighlight:setTextColor( 255, 255, 255, 20 )
else
labelHighlight:setTextColor( 255, 255, 255, 140 )
end
button:insert( labelHighlight, true )
labelHighlight.x = labelHighlight.x + 1.5; labelHighlight.y = labelHighlight.y + 1.5 + offset

local labelShadow = display.newText( params.text, 0, 0, font, size )
if ( textBrightness > 127) then
labelShadow:setTextColor( 0, 0, 0, 128 )
else
labelShadow:setTextColor( 0, 0, 0, 20 )
end
button:insert( labelShadow, true )
labelShadow.x = labelShadow.x - 1; labelShadow.y = labelShadow.y - 1 + offset
end

local labelText = display.newText( params.text, 0, 0, font, size )
labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )
button:insert( labelText, true )
labelText.y = labelText.y + offset
end

if ( params.onPress and ( type(params.onPress) == “function” ) ) then
button._onPress = params.onPress
end
if ( params.onRelease and ( type(params.onRelease) == “function” ) ) then
button._onRelease = params.onRelease
end

if (params.onEvent and ( type(params.onEvent) == “function” ) ) then
button._onEvent = params.onEvent
end

– Set button as a table listener by setting a table method and adding the button as its own table listener for “touch” events
button.touch = newButtonHandler
button:addEventListener( “touch”, button )

if params.x then
button.x = params.x
end

if params.y then
button.y = params.y
end

if params.id then
button._id = params.id
end

return button
end

– Label class

function newLabel( params )
local labelText
local size, font, textColor, align
local t = display.newGroup()

if ( params.bounds ) then
local bounds = params.bounds
local left = bounds[1]
local top = bounds[2]
local width = bounds[3]
local height = bounds[4]

if ( params.size and type(params.size) == “number” ) then size=params.size else size=20 end
if ( params.font ) then font=params.font else font=native.systemFontBold end
if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end
if ( params.offset and type(params.offset) == “number” ) then offset=params.offset else offset = 0 end
if ( params.align ) then align = params.align else align = “center” end

if ( params.text ) then
labelText = display.newText( params.text, 0, 0, font, size )
labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )
t:insert( labelText )
– TODO: handle no-initial-text case by creating a field with an empty string?

if ( align == “left” ) then
labelText.x = left + labelText.stageWidth * 0.5
elseif ( align == “right” ) then
labelText.x = (left + width) - labelText.stageWidth * 0.5
else
labelText.x = ((2 * left) + width) * 0.5
end
end

labelText.y = top + labelText.stageHeight * 0.5

– Public methods
function t:setText( newText )
if ( newText ) then
labelText.text = newText

if ( “left” == align ) then
labelText.x = left + labelText.stageWidth / 2
elseif ( “right” == align ) then
labelText.x = (left + width) - labelText.stageWidth / 2
else
labelText.x = ((2 * left) + width) / 2
end
end
end

function t:setTextColor( r, g, b, a )
local newR = 255
local newG = 255
local newB = 255
local newA = 255

if ( r and type® == “number” ) then newR = r end
if ( g and type(g) == “number” ) then newG = g end
if ( b and type(b) == “number” ) then newB = b end
if ( a and type(a) == “number” ) then newA = a end

labelText:setTextColor( r, g, b, a )
end
end

– Return instance (as display group)
return t

end
[/code] [import]uid: 74957 topic_id: 31827 reply_id: 127003[/import]

Ack!
http://developer.coronalabs.com/forum/2012/09/26/uilua-corona-simulator-919-and-922#comment-125398
Looks like ui is not supported. [import]uid: 74957 topic_id: 31827 reply_id: 127006[/import]

OK. Same behavior using widget.newButton

[code]


– main.lua


– Your code here
local widget = require “widget”

local PerformSomeAction = function (event)
–do nothing
end

local onButtonEvent = function (event )
if event.target.id == “One” then
alert = native.showAlert( “Button One Pressed”, “Try Button Two Next”, { “YES”, “NO” }, PerformSomeAction() )
end
if event.target.id == “Two” then
–do nothing
end
return true
end

local myFirstButton = widget.newButton{
id = “One”,
left = 100,
top = 200,
label = “Widget Button”,
width = 150, height = 28,
cornerRadius = 8,
onEvent = onButtonEvent
}
myFirstButton.x = display.contentCenterX
myFirstButton.y = 150
local mySecondButton = widget.newButton{
id = “Two”,
left = 100,
top = 200,
label = “Widget Button”,
width = 150, height = 28,
cornerRadius = 8,
onEvent = onButtonEvent
}

mySecondButton.x = display.contentCenterX
mySecondButton.y = 300
[/code] [import]uid: 74957 topic_id: 31827 reply_id: 127009[/import]

Try this:
[lua]local onButtonEvent = function (event )
if event.phase == “release” then
if event.target.id == “One” then
alert = native.showAlert( “Button One Pressed”, “Try Button Two Next”, { “YES”, “NO” }, PerformSomeAction() )
print “one”
elseif event.target.id == “Two” then
print “two”
end
end
end[/lua]

Without specifying the event phase, it will pass multiple requests to your function (press, moved, release) so by pressing it once it actually presses it twice (press & release). does that make sence?

Edit: You can use the above to get creative, maybe make some popup or animation if the button is pressed and then do your function once released. [import]uid: 62706 topic_id: 31827 reply_id: 127081[/import]

Thank you CraftyDeano. That works perfectly.
It makes complete sense, now that you have explained it, why it would work that way. Had the IDE handled my code the same way as my iOS device had, I might have figured this out for myself. The two different results threw me completely.

I really appreciate your help, it saved me a lot of time. [import]uid: 74957 topic_id: 31827 reply_id: 127091[/import]

No problem,

Glad it works and you understand it better :slight_smile: [import]uid: 62706 topic_id: 31827 reply_id: 127096[/import]