Lets work together to build a nice SearchBar sample app ...

And then … updated main.lua taking advantage of the inputText tutorial code :

display.setStatusBar( display.HiddenStatusBar ) --import the widget library local widget = require( "widget\_newTextField" ) local widget = require( "widget" ) -- create a constant for the left spacing of the row content local LEFT\_PADDING = 10 --Set the background to white display.setDefault( "background", 1, 1, 1 ) --Create a group to hold our widgets & images local widgetGroup = display.newGroup() -- The gradient used by the title bar local titleGradient = { type = 'gradient', color1 = { 189/255, 203/255, 220/255, 255/255 }, color2 = { 89/255, 116/255, 152/255, 255/255 }, direction = "down" } local inputFontSize = 18 local searchField local list local fieldHandler local searchBarActiveImage local tableViewYDelta = 30 local searchBarVisible = false -- Boolean used to track searchBar state local backButton, resetSearchButton, cancelButton local rowTitles = {} local eraseInitText = true -- Create toolbar to go at the top of the screen local titleBar = display.newRect( display.contentCenterX, 0, display.contentWidth, 32 ) titleBar:setFillColor( titleGradient ) titleBar.y = display.screenOriginY + titleBar.contentHeight \* 0.5 -- create embossed text to go on toolbar local titleText = display.newEmbossedText( "My List", display.contentCenterX, titleBar.y, native.systemFontBold, 20 ) -- create a shadow underneath the titlebar (for a nice touch) local shadow = display.newImage( "shadow.png" ) shadow.anchorX = 0; shadow.anchorY = 0 -- TopLeft anchor shadow.x, shadow.y = 0, titleBar.y + titleBar.contentHeight \* 0.5 shadow.xScale = 320 / shadow.contentWidth shadow.alpha = 0.45 --Text to show which item we selected local itemSelected = display.newText( "You selected", 0, 0, native.systemFontBold, 24 ) itemSelected:setFillColor( 0 ) itemSelected.x = display.contentWidth + itemSelected.contentWidth \* 0.5 itemSelected.y = display.contentCenterY widgetGroup:insert( itemSelected ) -- Handle row rendering local function onRowRender( event ) local phase = event.phase local row = event.row local isCategory = row.isCategory -- in graphics 2.0, the group contentWidth / contentHeight are initially 0, and expand once elements are inserted into the group. -- in order to use contentHeight properly, we cache the variable before inserting objects into the group local groupContentHeight = row.contentHeight if row.id == "searchBar" then local searchBarImage = display.newImageRect( row, "searchBar.png", 320, 44 ) searchBarImage.anchorX = 0 searchBarImage.anchorY = 0 searchBarImage.x = 0 searchBarImage.y = 0 else local rowTitle = display.newText( row, rowTitles[row.index], 0, 0, native.systemFontBold, 16 ) -- in Graphics 2.0, the row.x is the center of the row, no longer the top left. rowTitle.x = LEFT\_PADDING -- we also set the anchorX of the text to 0, so the object is x-anchored at the left rowTitle.anchorX = 0 rowTitle.y = groupContentHeight \* 0.5 rowTitle:setFillColor( 0, 0, 0 ) if not isCategory then local rowArrow = display.newImage( row, "rowArrow.png", false ) rowArrow.x = row.contentWidth - LEFT\_PADDING -- we set the image anchorX to 1, so the object is x-anchored at the right rowArrow.anchorX = 1 -- we set the image anchorX to 1, so the object is x-anchored at the right rowArrow.y = groupContentHeight \* 0.5 end end end -- -- Set up a function to handle the editing events and dismiss the keyboard when done. -- local function textFieldHandler( event ) -- -- event.text only exists during the editing phase to show what's being edited. -- It is \*\*NOT\*\* the field's .text attribute. That is event.target.text -- if event.phase == "began" then -- user begins editing textField print( "Begin editing", event.target.text ) -- check if we are supposed to clear the initial text supplied to the widget if eraseInitText then event.target.text = "" eraseInitText = false end elseif event.phase == "ended" or event.phase == "submitted" then -- do something with defaulField's text print( "Final Text: ", event.target.text) native.setKeyboardFocus( nil ) searchTextSubmitted( event.target.text ) elseif event.phase == "editing" then print( event.newCharacters ) print( event.oldText ) print( event.startPosition ) print( event.text ) end end local function searchBarActivate() -- -- Create an instance of the widget. -- searchField = widget.newTextField({ width = 183, height = 24, text = "Search", fontSize = 18, font = "HelveticaNeue-Light", cornerRadius = 0, strokeWidth = 1, inputType = "default", keyboardType = "search", autoPopKeyboard = true, --eraseInitText = true, listener = textFieldHandler, }) --searchField.anchorX = 0; searchField.anchorY = 0 searchField.x = 125 searchField.y = 22 searchField.alpha = 0 --eraseInitText = true -- Move tableView up by number of pixels specified in tableViewYDelta variable transition.to( list, { y= (-1 \* tableViewYDelta), time = 150, delta = "true" } ) -- Make the hidden Search UI elements visible gradually transition.to( searchBarActiveImage, { time=200, alpha=1 } ) transition.to( searchField, { time=200, alpha=1 } ) transition.to( cancelButton, { time=200, alpha=1 } ) transition.to( resetSearchButton, { time=200, alpha=1 } ) -- Bring keyboard up without waiting for user tap into Search text box native.setKeyboardFocus( searchField ) -- Lock the tableView so it does not respond to scrolling list.\_view.\_isVerticalScrollingDisabled = true searchBarVisible = true end -- Hande row touch events local function onRowTouch( event ) local phase = event.phase local row = event.target if "press" == phase then print( "Pressed row: " .. row.index ) elseif "release" == phase then -- Update the item selected text print( "Tapped and/or Released row: " .. row.index ) -- tap & release occured on 1st row which happens to be the search box. if row.index == 1 then -- Check if search box is already active if searchBarVisible == false then searchBarActivate() end else itemSelected.text = "You selected: " .. rowTitles[row.index] --Transition out the list, transition in the item selected text and the back button -- The table x origin refers to the center of the table in Graphics 2.0, so we translate with half the object's contentWidth --transition.to( list, { x = - list.contentWidth \* 0.5, time = 400, transition = easing.outExpo } ) transition.to( list, { x = - list.contentWidth \* 0.5, time = 400, transition = easing.outExpo } ) transition.to( itemSelected, { x = display.contentCenterX, time = 400, transition = easing.outExpo } ) transition.to( backButton, { alpha = 1, time = 400, transition = easing.outQuad } ) end end end -- Create a tableView list = widget.newTableView { top = 32, width = 320, height = 448, onRowRender = onRowRender, onRowTouch = onRowTouch, } searchBarActiveImage = display.newImageRect( widgetGroup, "searchBarEditing.png", 320, 44 ) searchBarActiveImage.anchorX = 0 searchBarActiveImage.anchorY = 0 searchBarActiveImage.x = 0 searchBarActiveImage.y = 0 searchBarActiveImage.alpha = 0 widgetGroup:insert( searchBarActiveImage ) --Insert widgets/images into a group widgetGroup:insert( list ) widgetGroup:insert( titleBar ) widgetGroup:insert( titleText ) widgetGroup:insert( shadow ) widgetGroup:insert( searchBarActiveImage ) local function searchTextSubmitted(searchText) print("Search text submitted " .. searchText) -- Hide keyboard native.setKeyboardFocus( nil ) -- Hide searchBox searchField.alpha = 0 searchBarActiveImage.alpha = 0 cancelButton.alpha = 0 resetSearchButton.alpha = 0 -- Move tableView down by number of pixels specified in tableViewYDelta variable transition.to( list, { y= tableViewYDelta, time = 200, delta = "true" } ) -- release the scroll lock on tableView list.\_view.\_isVerticalScrollingDisabled = false searchBarVisible = false -- Remove the native.textField so that it does not cause mischief --[[] if searchField then searchField:removeSelf() searchField = nil end ]]-- --searchField:removeSelf() end --Items to show in our list local listItems = { { title = "Breakfast", items = { "Coffee", "Bagel", "Cereal", "Toast" } }, { title = "Lunch", items = { "Sandwich", "Taco", "Noodles", "Soup", "Fries" } }, { title = "Dinner", items = { "Pizza", "Burger", "Steak", "Beef", "Lamb" } }, { title = "Desert", items = { "Apple Pie", "Ice Cream", "Cake", "Chocolate" } }, } --Handle the back button release event local function onCancelRelease() -- Hide Keyboard native.setKeyboardFocus( nil ) -- Hide searchBox searchBarActiveImage.alpha = 0 cancelButton.alpha = 0 resetSearchButton.alpha = 0 searchField.text = "" searchField.alpha = 0 -- Move tableView down by number of pixels specified in tableViewYDelta variable transition.to( list, { y= tableViewYDelta, time = 200, delta = "true" } ) -- release the scroll lock on tableView list.\_view.\_isVerticalScrollingDisabled = false searchBarVisible = false -- Remove the native.textField so that it does not cause mischief if searchField then searchField:removeSelf() searchField = nil end end --Create the Cancel search button cancelButton = widget.newButton { width = 50, height = 56, label = "Cancel", --labelYOffset = - 1, onRelease = onCancelRelease } cancelButton.alphaX, cancelButton.alphaY = 0, 0 cancelButton.alpha = 0 cancelButton.x = 284 cancelButton.y = 21 widgetGroup:insert( cancelButton ) --Handle the back button release event local function onResetRelease() print("Search field reset") searchField.text = "" end --Create the Cancel search button resetSearchButton = widget.newButton { width = 24, height = 24, label = "", defaultFile = "resetSearch.png", overFile = "resetSearch.png", --labelYOffset = - 1, onRelease = onResetRelease } resetSearchButton.alphaX, resetSearchButton.alphaY = 0, 0 resetSearchButton.alpha = 0 resetSearchButton.x = 234 resetSearchButton.y = 22 widgetGroup:insert( resetSearchButton ) --Handle the back button release event local function onBackRelease() --Transition in the list, transition out the item selected text and the back button -- The table x origin refers to the center of the table in Graphics 2.0, so we translate with half the object's contentWidth transition.to( list, { x = list.contentWidth \* 0.5, time = 400, transition = easing.outExpo } ) transition.to( itemSelected, { x = display.contentWidth + itemSelected.contentWidth \* 0.5, time = 400, transition = easing.outExpo } ) transition.to( backButton, { alpha = 0, time = 400, transition = easing.outQuad } ) end --Create the back button backButton = widget.newButton { width = 298, height = 56, label = "Back", labelYOffset = - 1, onRelease = onBackRelease } backButton.alpha = 0 backButton.x = display.contentCenterX backButton.y = display.contentHeight - backButton.contentHeight widgetGroup:insert( backButton ) rowTitles[1] = "Search" list:insertRow{ id = "searchBar", rowHeight = 42, isCategory = false, } for i = 1, #listItems do --Add the rows category title rowTitles[#rowTitles + 1] = listItems[i].title --Insert the category list:insertRow{ rowHeight = 24, rowColor = { default = { 150/255, 160/255, 180/255, 200/255 }, }, isCategory = true, } --Insert the item for j = 1, #listItems[i].items do --Add the rows item title rowTitles[#rowTitles + 1] = listItems[i].items[j] --Insert the item list:insertRow{ rowHeight = 40, isCategory = false, listener = onRowTouch } end end

Updated main.lua in first post with better transitions. Thanks to @jonjonsson for a great tip!

Site is claiming that the link for the main.lua.zip file:

http://forums.coronalabs.com/index.php?app=core&module=attach&section=attach&attach_id=1048

is an invalid URL…

fyi

Dewey

@dgaedcke - thanks for the heads up. I updated the file with an unzipped copy of the main.lua. This site does not permit uploads of lua files (funny…) so I renamed it to main.txt. Simply rename before use. Thanks

Updated version with

  1. better searchBar activation aesthetics
  2. checks implemented to only launch searchBar when its not launched. Row 1 was clickable even under the searchBar sometimes causing trouble. 
  3. some cleanup to commented code and some commenting for clarity… more to follow.

Use the attached main.txt and rename to main.lua. Enjoy!

Got some device testing. Not bad at all. The animations are not 100% exact but close enough to the IOS Mail app Search Bar.

I noticed that on the device the keyboard is not popping up initially when you tap Search so you need to tap again into the search field that appears… This can be fixed by putting the following in the searchBarActivate() function : 

        native.setKeyboardFocus( searchField )

Once you put this line in, the device behavior is closer to the native app experience where the keyboard pops up the second you tap the search bar at the top of your tableView.

Hope to get some feedback and even updates / suggestions. Don’t be shy now… :slight_smile:

This is really great work Ksan. Away from office at moment but really looking forward to digging into this when I get back on Monday

Hi Ksan,

Great to see community code, I will give it a try once I am back home.

Just wanted to ask - have you tried the widget.newSearchField?

Thanks
Atanas

Thanks guys. I look forward to your feedback. Its not much but its a start.

I did try the widget.newSearchField looking at the source to decipher its workings. I managed to get it to display and fire off the input field as well but the listeners didn’t seem to be complete. I think it will be great when CL eventually get this completed or released. Unfortunately I’m not proficient enough to try and improve it myself hence my simple ground-up approach.

The widget.newSearchField() isn’t in production yet.  I don’t know why and I’ve never tried to make it work, but in looking at the code, the “listener” that you pass to it is basically the same listener you would pass to native.newTextField().  We process the event first inside the widget, but then call  your function with the event table.  It appears that it’s your responsibility to handle the event for native.newTextField() and do with it what you want.

This could be as simple as waiting  until you get a “submitted” phase and then taking that string and looking things up in your data.  You could take the editing phase and do some time of search as you go feature but you would have to code up that functionality. 

Dunno if that nugget of info will help you try to work with our newSearchField() widget, but like I said, we didn’t make it live for a reason (and I don’t know what that reason is).

Rob

Thanks for the info Rob. Lets leave the newSearchField() widget out of this thread for now and keep this focused on a simple approach extending an existing CL sample app. This should be something anyone can pick and use once they have an understanding of the simple ListView2. In due course if an when CL updates and releases the newSearchField() widget then we can transition this sample to use it as well. 

Would love to get your feedback if you have a moment.

I just realized that creating the native.textField once and leaving it hidden when not in use might not be the best way to go forward. It is behaving a little funny in a storyBoard environment so perhaps removing it after use and recreating it again when needed might be a better approach. Will post another main.lua doing that later today.

Updated main.lua

Moved native.textField creation out of ORR into searchActivated function. 

Added code to remove native.textField once search is submitted or cancelled. 

When using storyboard, you should always create native objects in the enterScene() function and remove them in exitScene().   As far as leaving them around, if you want to do that, you would create it in main and make it some kind of global (i.e. add it to your mydata table since we don’t want real globals).  Then the difference scenes could reference it.

Rob

Got it. Thanks for the hints. The ListView2 sample does not use storyboard so my sample posted here does not either but in parallel I’m putting the searchBar into my storyboard based app so trying to keep as much reused as possible in best practices. So for now I just recreate native.textField when it’s needed and then dispose it when search is cancelled or submitted. Seems to work well.

Hi Ksan,

The event firs fine, however I have not tested the newSearchField on multiple devices yet. The code itself is pretty small and can be easily fixed if we encounter issues with it (the main issue being still the native fields staying on top, but thats something I am working on for a set of editFields as part of a widget library)

Here is the code that I use to create a search field in the toolbar:

    local function onSearchEvent(event)

      if event.phase == “submitted” then

          local search = event.target.text;

          if search and search:len() > 0 then

            local sFilter = event.target.text and ‘PLAYERS.firstName LIKE "’…search …

                                                   ‘" OR PLAYERS.lastName LIKE "’…search …’"’

                                                or ‘’;   

              

           myApp.dbPlayers:setFilter(sFilter)

         else

           myApp.dbPlayers:setFilter(nil);

         end  

         self:enterScene(nil)

       end  

    end;

        local searchField = widget.newSearchField

                              {

                              left = 0,

                              top = 0,

                              width = 200,

                              placeholder = “Search " …title…”…",

                              textFieldXOffset = 10,

                              listener = onSearchEvent,

                            }

        

        searchField.anchorX = 0.5;searchField.anchorY = 0.5; 

        searchField.x = display.contentCenterX

        searchField.y = titleBar.height * 0.5 + display.topStatusBarContentHeight

        parent:insert( searchField)

Hi atanas, thanks for sharing! I was getting stuck with the listener for some reason but I will give this another try. If we can get the official widget to work all the better of course. Thanks for your contributions by the way. Wow!!!

Trying to run this example. Still need shadow.png, rowArrow.png, Can you point me to those?

Rich

Thanks for checking it out.

This search sample is based on Corona Labs ListView2 Demo App. You can find this in Corona Samples / Interface on your computer. The images you mention are part of that sample so I did not bother uploading them here again. The additional required images are attached to the 1st post in the thread. Use the most recent main.lua. The one in 1st post is outdated. 

Yes, the native items should be in the enterScene(), but would also like to add that it (especially ‘textField’) be placed in a delay (i did it with delay + alpha transition). This is to prevent the textField from appearing suddenly on top of the screen (just a split second), and relocated to the correct x/y position. Some users may thin it is a glitch, but it is actually some re-drawing/rendering done.

Good tip. Thank you very much. The sample here addresses this issue in a transition.to call in the following manner : 

transition.to( searchField, { time=200, alpha=1 } )

searchField is initially created with an alpha=0.