Picker Wheel Widget Question

I notice with the picker wheel (Using the iOS 7 theme) that when you tap on the row that is selected it has a similar effect to what a widget button does when it gets tapped (When changes color when being touched, changes back when lifted off to simulate pushing something) the row changes to a grey colour while being tapped.

But when I look in the documentation there is no method for the pickerwheel to do anything when the selected row is tapped. Is there a hidden function for this that I could use?

Hi @AppOwlDev,

I’m a bit unclear on what you want to do here. Typically, picker wheels allow a user to select a combination of values (one from each column) and then a separate button or event is used to gather that data and perform the desired action. What kind of behavior do you want when the user taps on a row?

Thanks,

Brent

My pickerwheel just has 1 column of information, that’s all I need. I’m using the getValues method right now, with a button I created. What I’m saying is I noticed that when you tap the currently selected row on the pickerWheel, at least when it only has one column like mine, the row changes colour while you are pressing, much like a button widget responds.

So I was wondering if there is a on touch method that I can utilize that is built in.

Hi @AppOwlDev,

Well there’s not a built-in method for that, but most widgets can be dissected to some degree such that you could implement it yourself.

Out of curiosity, how do you want to handle these “taps”? I know the option color/label changes when you tap on it, but it does that for both the option that is currently selected (in the middle of the picker) and also for the outlying options… in that case, that option gets selected and moves to the middle. So, do you want to use that as the “picked option” or only detect when the user clicks on the row that is currently in the middle?

Brent

Hi Brent,

I’d probably want it to work like this:

If you tap outside the selected row, it moves the tapped row to the middle (Selected). If you tap the selected row, it does something with the contents of that row.

In my case, when I tap the selected (Middle) row, I would want the widget to move off screen, and the contents of that row be copied to a waiting text label. I have already achieved this with a separate button I created and “attached” to the picker widget. But I think it would look cleaner without the little button.

Is it possible to override the function that handles the colour change of the selected row? I would add my code to the “on release” part of the event.

Hi @AppOwlDev,

Well, there are two ways I can think to achieve this: one being fairly intensive, the other using a little simple trickery.

  1. Dig into the widget library (which is open source) and add a new function that detects tapping on the row. This would be the superior method, but unless you’re familiar with the widget library and have tinkered with its mechanics before, you might get in too deep.

  2. Place an invisible but touch-sensitive (.isHitTestable=true) vector rectangle (display.newRect()) exactly over the middle row of the picker. Add a “tap” listener to it (not “touch”) and then, in that handling function, be sure to not"return true" so that the tap can still propagate through to the picker itself. On this event, you know that the user tapped on the middle row (or so it appears to the user) and then you can use the typical :getValues() call on the picker to gather the info from the currently selected row.

Hope this helps,

Brent

Hi Brent, that helps a lot, thanks!

How does the “Return True” part factor in? I’ve never really fully understood when not to use it. Is there a blog posting or tutorial that explains exactly what it does?

Hi @AppOwlDev,

Fortunately yes, there is a guide on how “return true” is important in respect to taps/touches. See the section “Understanding Hit Events”.

http://docs.coronalabs.com/guide/events/detectEvents/index.html#understanding-hit-events

Take care,

Brent

Hi Brent,

Thanks for the link, very helpful.

So i’m trying to use one of your suggestions and I can’t seem to get it to work.

I have created a pickerWheel and then created a simple rectangle display object the same size and positioned it over top of the pickerwheel. I gave the rectangle a “Touch” listener with a simple print command in the “ended” phase for testing. I also added isHitTestable = true and I have NOT added return true.

The problem is that while the touch event goes right through the rectangle to the picker widget below, allowing the user to mive the wheel (yay!) it does not register the touch event for the rectangle itself (boo…), it’s as if the rectangle doesn’t exist. If I add the return true, it only registers the rectangle touch event (As it’s supposed to).

Now if I change the listener type to Tap instead, it works perfectly. But that isn’t helpful since a user is going to move his/her finger up and down to move the picker widget, therefor not registering as a “tap”.

local function wheelTouch(event) local phase = event.phase if phase == "ended" then print("touched") end end local columnData = { { startIndex = 3, labels = { "#0 - 0.06", "#1 - 0.073", "#2 - 0.086", "#3 - 0.099", "#4 - 0.112", "#5 - 0.125", "#6 - 0.138", "#8 - 0.164", "#10 - 0.19", "#12 - 0.215" } } } diaWheel = widget.newPickerWheel { columns = columnData, } diaWheel.anchorX = 0 diaWheel.anchorY = 0.5 diaWheel.x = -50 diaWheel.y = display.contentCenterY local touchBox = display.newRect( 0, 0, diaWheel.contentWidth, diaWheel.contentHeight ) touchBox.anchorY = 0.5 touchBox.anchorX = 0 touchBox.x = diaWheel.x touchBox.y = diaWheel.y touchBox.isHitTestable = true touchBox:addEventListener( "touch", wheelTouch ) touchBox.alpha = 0 screenGroup:insert( 1, diaWheel ) screenGroup:insert( 2, touchBox )

Hi @AppOwlDev,

I think your touch rectangle is behind your picker wheel because of how you’ve inserted and used the indexing (1, 2, etc.). If you set the fill color of the rectangle to red ( “touchBox:setFillColor(1,0,0)” ) and set its alpha to non-0, do you see it in front of the wheel? If not, then it’s behind, and naturally it would not detect the touch because the picker “consumes” it.

If you insert it without those index numbers, but in the same order you’re doing (in lines) then it should reside in front. But if you want to be sure, bring it to the front using “touchBox:toFront()” AFTER you insert it into “screenGroup”.

Brent

Hi Brent,

My numbering is correct, the touchBox is in front of the picker widget. I changed it to bright red to double check. I also just used the :toFront() command to triple make sure, and it still doesn’t work.

Here is a test program you can run to see:

local composer = require( "composer" ) local scene = composer.newScene() local widget = require ( "widget" ) local function testFunction(event) if event.phase == "ended" then print("was touched!") end end function scene:create( event ) local sceneGroup = self.view local columnData = { { startIndex = 3, labels = { "Monday", "Tuesday", "Wednesday", "Thursday", "TGIF" } } } local testWheel = widget.newPickerWheel { columns = columnData, } testWheel.x = display.contentCenterX testWheel.y = display.contentCenterY sceneGroup:insert( testWheel ) local testBox = display.newRect( sceneGroup, 0, 0, testWheel.contentWidth, testWheel.contentHeight ) testBox.x = testWheel.x testBox.y = testWheel.y testBox:setFillColor( 1, 0, 0 ) sceneGroup:insert( testBox ) testBox:addEventListener( "touch", testFunction ) testBox.isHitTestable = true testBox.alpha = 0.5 end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then elseif ( phase == "did" ) then end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then elseif ( phase == "did" ) then end end function scene:destroy( event ) local sceneGroup = self.view end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) return scene

Hi @AppOwlDev,

Hmmm, I tested this and you’re correct. The “began” phase is registered on the rectangle, but the “ended” phase is not. Apparently the picker consumes that and the rectangle never knows that you lifted off it.

Before I continue, I should note that my original suggestion was to use a smaller rectangle that only covers the middle row of the picker. You have it covering the entire wheel, but my intention was not so.

Now, stepping back earlier into this thread, I think a “tap” listener on that rectangle gives you the functionality you want, for these reasons:

  1. If the user touches the middle row and then moves the touch, the “tap” is ignored. This is important because the user would very likely do that to select a different option (you couldn’t expect them to always touch outside the middle row to move the wheel).

  2. If the user touches or taps outside of the middle row (so, not on the rectangle), the wheel will behave as expected, and they can tap an exterior row to move it to the middle.

  3. When the user taps the middle row, you get the expected behavior of them choosing that value. The tap is registered and you can proceed with :getValues().

Let me know if there’s a case I’m not thinking of…

Brent

Hi Brent,

That would work… except I forgot to mention I looking at it for a slightly different function now. What I would like is for an outside source (Let’s say a newText) to update with the getValues() value of the pickerWheel as the pickerWheel is being used.

So what I thought of is for the touchBox eventListener to (After a very slight delay) call the getValue() method in the “ended” phase, after the user lifts their finger off the testBox and Picker presumably with their choice.

I would think the tap event would still work for that. Just update the newText item when a tap is registered on the rectangle…

When I get home I can try again. But I don’t think that helps in your first example:

  1. If the user touches the middle row and then moves the touch, the “tap” is ignored. This is important because the user would very likely do that to select a different option (you couldn’t expect them to always touch outside the middle row to move the wheel).

If they touch then move their finger to move the wheel, and then lift off, the “tap” event will not register and the newText will not update. This is actually a different pickerWheel than my original post was about (I just decided to go with a separate button for that one).

I don’t want the user to have to tap or press the pickerWheel a second time like it’s a button. I want the newText to update automagically as the user moves the pickerWheel, or after the user lifts their finger from moving the pickerWheel.

Shouldn’t the removal of Return True accomplish that? Or is this a bug?

Well, this is getting into some fairly advanced (and unusual) usage of the picker wheel. I think you’d almost have to detect the “moved” phase of the rectangle, or else do a constant Runtime listener which checks the values and detects if it has changed from the previous value (the wheel has been shifted one notch).

Brent

Hi @AppOwlDev,

I’m a bit unclear on what you want to do here. Typically, picker wheels allow a user to select a combination of values (one from each column) and then a separate button or event is used to gather that data and perform the desired action. What kind of behavior do you want when the user taps on a row?

Thanks,

Brent

My pickerwheel just has 1 column of information, that’s all I need. I’m using the getValues method right now, with a button I created. What I’m saying is I noticed that when you tap the currently selected row on the pickerWheel, at least when it only has one column like mine, the row changes colour while you are pressing, much like a button widget responds.

So I was wondering if there is a on touch method that I can utilize that is built in.

Hi @AppOwlDev,

Well there’s not a built-in method for that, but most widgets can be dissected to some degree such that you could implement it yourself.

Out of curiosity, how do you want to handle these “taps”? I know the option color/label changes when you tap on it, but it does that for both the option that is currently selected (in the middle of the picker) and also for the outlying options… in that case, that option gets selected and moves to the middle. So, do you want to use that as the “picked option” or only detect when the user clicks on the row that is currently in the middle?

Brent

Hi Brent,

I’d probably want it to work like this:

If you tap outside the selected row, it moves the tapped row to the middle (Selected). If you tap the selected row, it does something with the contents of that row.

In my case, when I tap the selected (Middle) row, I would want the widget to move off screen, and the contents of that row be copied to a waiting text label. I have already achieved this with a separate button I created and “attached” to the picker widget. But I think it would look cleaner without the little button.

Is it possible to override the function that handles the colour change of the selected row? I would add my code to the “on release” part of the event.