Creating a native scroller to scroll a field

In this lesson we will see how to set up a native mobile scroller to allow you to scroll the contents of a field.

You can download the stack for this lesson here: https://tinyurl.com/y9whje9j

Create your content

Create your content

The first step is to add the content you wish to display to the stack. In this case we are using a field to display text. Make the field large enough to show all the text. From the size and position tab in the property inspector (1), lock the size and position of the field (2).

Create a group

Create a group

Now group your field and set the size of the group to the area you want to be shown in your app. Lock the size and position of the group.

The group will be used to set the size of the scrollable area.

Create the scroller

We will set up the scroller in the preOpenCard handler of the card script. Any messages sent by the scroller are sent to the control that created it so we can also handle the messages in the card script.

Firstly we check the environment as we only want to create the scroller on mobile.

NOTE: mobile controls do not work in the IDE.

if environment() is not "mobile" then exit preOpenCard

We then work out what area the scroller should work over and the area of the content to be scrolled. The scroller should take up the same area as the group and should allow the full area of the field to be scrolled into view.

put the rect of group "scrollArea" into tScrollerRect
put the left of field "lorem",the top of field "lorem",the right of field "lorem",the top of field "lorem" + the formattedHeight of field "lorem" into tContentRect

If you experience hidden text in your field after a scroll please replace the above script with :

put 0,0,(the formattedWidth of group "scrollArea"),(the formattedHeight of group "scrollArea") into tContentRect

Now create the scroller and store the ID of the created control in a script local variable

mobileControlCreate "scroller", "loremScroll"

Set up the properties of the scroller

We need to set the properties of the scroller so it behaves as we want. We set the rect, the content rect, the visibility, whether the scroll indicator shows and the initial scroll.

mobileControlSet "loremScroll", "rect", tScrollerRect
mobileControlSet "loremScroll", "contentRect", tContentRect
mobileControlSet "loremScroll", "visible", true
mobileControlSet "loremScroll", "scrollingEnabled", true
mobileControlSet "loremScroll", "vIndicator", true
mobileControlSet "loremScroll", "vscroll", 0

Handle the scroll message

The scroller sends messages which you can handle to implement scrolling. In this case we want to set the vScroll of the group to scroll the content into view

on scrollerDidScroll hOffset, vOffset
   // When the user scrolls move the displayed content
   set the vScroll of group "scrollArea" to vOffset
end scrollerDidScroll

Scrolling to a particular line

You can scroll to a particular line by setting the vScroll of of the scroller.

Given a line number and the textHeight of the "lorem" field you can calculate the correct scroll value.

mobileControlSet "loremScroll", "vscroll", (tLine * the effective textHeight of field "lorem")

21 Comments

TG

Thank you for that nice example.
Is there a way to use it with a list with selectable items?
I am working with such a list and it happens of course a selection on mouseUp when i want just scroll. how could i implement a logic that avoids that so a line gets selected just when i tip on it but not when scrolling. Thanks.

Hanson Schmidt-Cornelius

Hi TG,

from your comment I take it that you have already implemented the functionality but are now just wondering how to handle the mouseUp message.

You could use the mouseLoc to get the X,Y location of the mouse in the mouseDown and then mouseUp handlers and then test if there is a significant distance between these locations. If there is, then you have a scroll.

Measuring the X or Y shift may be sufficient, if not, use Pythagoras to get the distance in any direction.

Kind Regards,

Hanson

Kevin

Thanks so far :-)
I've been trying for a while now to use that principle on a datagrid with form-style (rather than on a simple field as in the example above). However, I couldn't make it work...
Could you please show us / give an example how to use the mobile scroller in a datagrid? I've searched the forum all morning but couldn't find a working example...

Many thanks in advance

Kevin

Hanson Schmidt-Cornelius

Hi Kevin,

that is an interesting concept.
Not quite sure how well this would work as the datagrid manages its own scrolling. In effect you could have two sets of scrollers to contend with.

I am assuming that you are trying to utilize the dynamic layout configuration of the datagrid but at the same time want to provide a more native scrolling experience.

My suggestion would be to make the datagrid as big as you need it to be so you can fit all of the data on one page, that way removing its own scrollers. The result could then be added to a group that is controlled by the native mobile scrollers.

I have not tested this, but it would be the approach I would initially consider.

Kind Regards,

Hanson

Daniel DuPont

One of my problems is the text is not fixed. I want to display a push notice in the field. Not a problem, really. But since adding the scroller above, the problem comes with different screen sizes and orientations. My layouts got weird after adding the scroller. I got the layout fixed, but only for portrait. After that the touch scroller fails now.

I am a beginner developing for Android (iPhone later if I ever get this app to work). Any help is appreciated. Thank you & Cheers!

P.S. Haven't been to Scotland since 2001, I miss it there.

Hanson Schmidt-Cornelius

Hi Daniel,

I am not sure what you mean when you say that your layouts got weird.
Can you please provide some more details one what exactly is not working for you.

Kind Regards,

Hanson

Malte

Is there any reason the field needs to be in a group?

local sScrollerID
on preOpenCard
local tScrollerRect,tContentRect
if environment() is not "mobile" then exit preOpenCard
mobileControlCreate "scroller", "loremScroll"
put the result into sScrollerID
put the rect of fld "scrollme" into tScrollerRect
put the topleft of fld "scrollMe" & "," & the right of fld "scrollme"&","&( the top of fld "scrollme" + the formattedHeight of fld "scrollme") into tContentRect
mobileControlSet "loremScroll", "rect", tScrollerRect
mobileControlSet "loremScroll", "contentRect", tContentRect
mobileControlSet "loremScroll", "visible", true
mobileControlSet "loremScroll", "scrollingEnabled", true
mobileControlSet "loremScroll", "vIndicator", true
mobileControlSet "loremScroll", "vscroll", 0
end preOpenCard

on scrollerDidScroll hOffset, vOffset
// When the user scrolls move the displayed content
set the vScroll of fld "scrollMe" to vOffset
end scrollerDidScroll

Seems to work ok?

Elanor Buchanan

Hi Malte

No, the field doesn't need to be in a group. Your method will work equally well.

Kind regards

Elanor

Steve Bennett

Good example, but there is an error in the code.

If you move the group down the card and with fewer lines of text, the scroller either doesn't scroll or only partially scrolls.

The problem is the line:

put the left of field "lorem",the top of field "lorem",the right of field "lorem",the formattedHeight of field "lorem" into tContentRect

A rect is defined by left,top,right,bottom, but the formattedHeight property returns the height not the bottom of an object.

With the group at the top of the card the formattedHeight property is almost the same value as bottom and the example works, but as the group is moved down the card the 2 values diverge and the scroller behaviour is more erractic.

With the group lower down the card and using fewer lines of text, by using formattedHeight alone for the value of bottom it is possible for the rect value for bottom to be less than the value for top, and the scroller will no longer work.

The line should read:

put the left of field "lorem",the top of field "lorem",the right of field "lorem",the top of field "lorem" + the formattedHeight of field "lorem" into tContentRect

Elanor Buchanan

Dear Steve Bennet,

Thanks for bringing that error to our attention. I have correct the code in the lesson.

Kind regards

Elanor

Kim

Hi

Whats the purpose of "put the result into sScrollerID"? sScrollerID doesn't get used again in the example. Is it to provide an ID that can subsequently be used with mobileControlDelete? If that's the case, wouldn't mobileControlDelete "loremScroll" achieve the same?

Regards

Kim

Elanor Buchanan

Hi Kim

You are right, it is not needed. The control can be referred to by name. When these native controls were first added they could only be referred to by ID and I think this part of the lesson is a remnant of that old requirement.

I have updated the lesson and attached stack to refer to the scroller by name throughout.

Thanks for bringing this to our attention.

Kind regards

Elanor

Trevix

Would be nice to add how to scroll by script to the end or to a specific line

Elanor Buchanan

Hi Trevix

I have added another section to the end of the lesson that explains how to do this.

I hope that helps.

Elanor

Freek

Thanks for this lesson and the comments, they helped to make the scrolling beter than it was and I learned some new stuff :), however there are still some things I like to tune and I can't seem to figure out how to do them.
The content bounces at the bottom but not at the top.
When I arrive at the bottom and drag upwards this only works for a certain distance. When I drag it up further the content stops moving and when I release there is a short period in which nothing happens, it seems to freeze, it feels a bit off. When a smaller movement is made it works fine though.
Any help is welcome and I hope the descriptions make sense.

Elanor Buchanan

Hi Freek,

If you have "canBounce" set to true it should work, and I am not sure what the issue with dragging from the bottom could be. Could you let us know if you see this in the attached example stack and what OS and device you are using so we can take a look.

Thank you.

Kind regards

Elanor

Freek

Hi Elanor,

Thanks for your reply.
I'm using a MacBook Air with Mojave 10.14.4; LiveCode Indy 9.6.0 (dp 3); Xcode 11.3.1; and a iPhone 11 pro Max 13.3 simulator. Hope that helps.
With the example stack the same things happen, also with
mobileControlSet "loremScroll", "canBounce", true
The bottom does bounce, but the top doesn't.
With regard to the dragging from the bottom:
I'm going to do my best in putting it into words, but I don't know the proper terms. For example, when using a touchpad from a Mac, when one is within the boundaries of a page and one moves its fingers from one edge of the mousepad to the edge on the other side of the mousepad, that corresponds to roughly a scroll with the size of the screen. When one is at the boundary of the page and makes the same movement on the mousepad, the corresponding movement on the screen is about 1/5 - 1/10 of the screen.
In the simulator this does work for a certain amount of drag, but it stops dragging before arriving at the boundary.

These are some minor details, and the way of scrolling in my app has improved so I'm glad about that. However for future apps I rather have small details right all the time, than having a small "mistake" each time.

If I somehow find out where the issue lies or how to solve it, I"ll let you guys now and I would be grateful if you beat me to it :).

Ciaociao!

Elanor Buchanan

Hi,

I think you are probably experiencing this bug

https://quality.livecode.com/show_bug.cgi?id=21456

There are some workarounds suggested in the discussion on the bug report.

I hope that helps,

Kind regards

Elanor

Jeff

How do you set the vscroll of a native scroller when the field it is connected to is not at the beginning of the text?

This code always sets the vscroll to 0:
mobileControlSet "loremScroll", "vscroll", 0

So if you add a second card to the example stack, scroll half-way down the text, switch to the second card) which destroys the scoller) and switch back to the first card (which recreates the scroller), the scroller will NOT let you scroll back up because the present text position is set to 0.

So I saved the vscroll on "closeCard" before the scroller was deleted, and then assigned it to the scroller when it is recreated with:
mobileControlSet "loremScroll", "vscroll", savedScrollValue
But the mobile scroller is still set to 0.

If I run:
mobileControlGet("loremScroll", "vscroll")
after I set the value, it returns that the scroller is set to the value of "savedScrollValue". But when you try to scroll, you see that the scroller is at 0.

I've tried:
mobileControlSet "loremScroll", "vscroll", savedScrollValue
I've tried:
set the vScroll of group "scrollArea" to savedScrollValue
I've tried setting the vscroll of the field "lorem", nothing works. The mobile scroller always starts at 0 no matter what I assign it to. How can I tell the scroller that we not starting at vscroll = 0?

Panos Merakos

Hello Jeff,

What you describe is a known issue. See https://quality.livecode.com/show_bug.cgi?id=22453 for more details and how to work this around. Short answer: add a "wait 0 milliseconds " just before mobileControlSet "loremScroll", "vscroll", savedScrollValue

Kind regards,
Panos

Jeff

THANK YOU! I don't think I ever would have figured that out myself. :-)

I'm sorry my "googling" of the issue didn't pull up the bug. I so appreciate the quick response.

Jeff

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.