Skip to content

iOSTableView Web Control Redux – Part 3

Part 3 of our three part series on building an iOSTableView control for use in web projects using the WebSDK. If you haven’t already, please read parts 1 and 2.

We’re finally coming to the end of our iOSTableView control. Over the last two sessions, we’ve designed a relatively simple HTML structure for our control and gone from the appearance of an unordered bulleted list to something that actually looks like an iOSTableView. The next step is to get the control to act right…and we’ll be doing that with a little more CSS and some javascript. There’s still a fair bit of work to do, so lets get started.

The first hurdle is that our control doesn’t actually scroll. Since we know that there are 30 groups on our control we should be able to scroll down to get to the other items. Go back to the iOSTableView.SetupCSS and add this code just below the first two lines to tell the control to scroll in the Y direction when it overflows:

styles(0).value("visibility") = "visible"
styles(0).Value("background-color") = "#FFFFFF"
styles(0).Value("overflow-y") = "scroll"

If you run the project now, you’ll find that on the desktop it scrolls very nicely. If you’re using a Mac with a trackpad and a recent version of OS X, you even get kinetic scrolling for free. Now try connecting from an iOS device (the iOS simulator will work too). Neat huh? Scrolling “works” except that there’s no kinetic scrolling. We need to add one more style to enable that:

styles(0).value("visibility") = "visible"
styles(0).Value("background-color") = "#FFFFFF"
styles(0).Value("overflow-y") = "scroll"
styles(0).Value("-webkit-overflow-scrolling") = "touch"

There we go. Now the scrolling behavior acts right on the iOS devices as well (Android too if you’re curious).

Now that we’ve got scrolling working, have you ever noticed the effect on an iOSTableView where the headers stick to the top of the screen until the one below it bumps up against it? Apple added an attribute so we can simulate that effect. In the code for the headers, you need to add a value for position like this:

dim st as new WebControlCSSst.Selector = ".iOSTableView_header"
st.Value("padding") = "5px"
st.Value("background-color") = "rgba(240,240,240,0.9)"
st.Value("top") = "0px"
st.Value("position") = "-webkit-sticky"
styles.Append st

This effect also relies on the headers being “pulled” up by their containing element. At the moment, that’s the entire control, so we’ll need to add another div around each group. In iOSTableViewGroup.Render, add the following lines:

dim sa() as string
sa.Append "<div>"
sa.Append "<div class=""iOSTableView_header"">" + self.Caption + "</div>"
sa.Append "<ul>"
for i as integer = 0 to UBound(items)
  sa.Append items(i).Render()
next i
sa.Append "</ul>"
sa.Append "</div>"
return join(sa, EndOfLine)

If you run the project now, the date headers will actually stick to the top of the view until the next one comes along! We’re not out of the woods yet though. There’s still that nasty issue of what happens if you pull downward first, because it acts like you’re pulling the whole web page down with it… and in fact, you are.

Note: While working on the attendee app for XDC, we ran into this very same issue. After several days of trial and error and several hours of searching on the internet to see if anyone had come up with a solution to this problem, I contacted a buddy of mine who had solved this in one of his projects. He said “Oh yeah, that. You need to wrap the control in two levels of scrolling divs to prevent that page bounce.” It had apparently taken him several weeks of trial and error to figure that out. The moral of the story? Ask your friends, colleagues and the community when you run into an issue that you can’t figure out. You might find someone that’s gone down this path before you.

So let’s add a second div inside the outer control div. In iOSTableView.SetupHTML:

dim sa() as string
sa.Append "<div id=""" + self.ControlID + """ class=""iOSTableView"">"
sa.append "<div class=""iOSTableView_scrollarea"">"
for i as integer = 0 to UBound(groups)
  sa.Append groups(i).Render()
sa.Append "</div>"
sa.Append "</div>"
return join(sa, EndOfLine)

Now a little CSS magic. In iOSTableView.SetupCSS, only add the overflow-y and -webkit-overflow-scrolling properties if the browser is SafariMobile or Android. If we don’t do this, the desktop browsers will get double scrollbars:

styles(0).value("visibility") = "visible"
styles(0).Value("background-color") = "#FFFFFF"
select case session.Browser
case websession.BrowserType.SafariMobile, websession.BrowserType.Android
  styles(0).Value("overflow-y") = "scroll"
  styles(0).Value("-webkit-overflow-scrolling") = "touch"
end select

Then add another style definition to the event:

st = new WebControlCSS
st.Selector = ".iOSTableView_scrollarea"
st.Value("overflow-y") = "scroll"
st.Value("-webkit-overflow-scrolling") = "touch"
st.Value("width") = "100%"
st.Value("height") = "100%"
styles.Append st

Run the project again and check it on the desktop and mobile devices. Now if the user starts by dragging downward, only the control scrolls!

This control is useless if you can’t tell which rows the user has touched. We’ve still got a few last pieces to add to make this work.

Go back to the iOSTableView class and add an event handler for ExecuteEvent (if you haven’t already). We need to make it so the control can handle an event coming from the browser. Enter the following code:

select case Name
case "rowclicked"
  RaiseEvent RowClicked(parameters(0))
end select

…and add the event definition for the event that we’re raising…

Event RowClicked(tag as Variant)

So what we’re going to do is add links to each of the rows. This brings up an interesting flaw in our original design. The innermost items, where the actual links are going to be called, has no idea about the Control ID of the control which contains it. We need to send that data down through the control at render time. In iOSTableViewItem.Render, add a parameter: ParentControlID as String. Then add these two rows to the method:

dim sa() as string
sa.Append "<li class=""iOSTableViewRow"">"
sa.Append "<a href=""javascript:Xojo.triggerServerEvent('" + _
   ParentControlID + "','rowclicked',['" + _  
   tag.StringValue.ReplaceAll("'","'")+ "']);"">"
sa.Append self.Caption
sa.Append "</a>"
sa.Append "</li>"
return join(sa, "")

Now we go up one level to iOSTableViewGroup and make it so we can pass along the ParentControlID parameter. Add a parameter to iOSTableViewGroup.Render: ParentControlID as String. Then add the parameter to the call to render the child nodes:

sa.Append items(i).Render(ParentControlID)

Add the Control ID to the call to the group’s Render method in iOSTableView.SetupHTML:

sa.Append groups(i).Render(self.ControlID)

Last but not least, we need to add a little more code to SetupCSS to keep the links from changing color:

st = new WebControlCSS
st.Selector = ".iOSTableView ul li.iOSTableViewRow a"
st.Value("color") = "#000"
st.Value("text-decoration") = "none"
styles.Append st

Now go back to WebPage1, add a Label to the layout and then implement the RowClicked event on the iOSTableView Instance. If you set the code to something like this:

Label1.Text = "Item " + Str(Tag.IntegerValue)

When you run the project, clicking or touching the rows will change the label based on the row.

Well, that’s it! Now you have a fully functional iOSTableView which you can use in your projects which looks and feels like a native control.