Change canvas heights based on text drawn inside

In my desktop app (multiple choice exam), I have several canvases (1 for the question and 4 for the answer choices). These resize based on the text that is present in the canvas with the use of drawing a picture of the text. This works well. I am now trying to accomplish the same in iOS but am not quite getting it right. The Q and As are in a container control, which is embedded in a ScrollableArea on the screen. The code below is in a method in the container control and is just for the question. Figured if I can get this to work, I can get the answer choices to do the same. The question’s height is set to min 25 ( >= 25), not sure if this matters. The first run works well (displays an appropriate height of the canvas to fit all the text), but when I navigate through the questions, if the next one is less text, the canvas does get smaller accordingly, but if a future question is lengthier, it does not expand and stays at the last smallest height. What am I missing?

dim tempQHeight as new Picture(canQuestion.Width, canQuestion.Height)

dim htQ as Double

txQuestion = rs.Column("Question").StringValue 'set the text
tempQHeight.Graphics.Font = font.SystemFont(18) 'set size
htQ = tempQHeight.Graphics.TextHeight(txQuestion, canQuestion.Width) 'get height of this text with wrapping

dim constHgtQ as new iOSLayoutConstraint(canQuestion, _
iOSLayoutConstraint.AttributeTypes.Height, _
iOSLayoutConstraint.RelationTypes.Equal, _
nil, _
iOSLayoutConstraint.AttributeTypes.None, _
1.0, _
htQ + 25)
canQuestion.AddConstraint(constHgtQ)

canQuestion.Refresh

Why use a canvas to draw text?

You lose ability for people with accessibility problems to select the text and have the device speak out what’s written.

As I understand, the user should be able to click on one of the four answers. This is definitely what a button is supposed to do.
To change the styling of the button you can either use declares or iOSdesignExtensions which is free on github.

Thanks Jeremie. I chose the canvas because I want the user to tap on the answer choice to make their selection. In my old app, which I am replacing, I have a canvas next to a label (tap to make a selection), and the label contains the choice. Also canvas over a button because some text in the answer choices can actually be lengthy, more than one line of text. I am not aware of a button being able to support text wrapping, or can it?

Yes, it is possible.

I will share an example when I get back to my computer.

I would be interested in seeing if this would work but also might still like to explore a solution with using the canvas. Thanks Jeremie!

So actually I was wrong… :face_with_hand_over_mouth:
I thought that iOS could automatically adapt the height depending on the length of the caption. But it doesn’t seem to work.

Anyway, if your captions aren’t too long, you could certainly use something like this:

Using iOSDesignExtensions
Add a button in a MobileScreen and set the Height constraint to 70.

In the Open event of the button add this code:

Button1.SetLineBreakModeXC(ControlExtensionsXC.NSLineBreakMode.TruncateTail) //This works better than wordwrap in this situation
Button1.SetTextAlignmentXC(ControlExtensionsXC.NSTextAlignment.center) //Aligns the text in the center
Button1.SetNumberOfLinesXC(3) //Forces the button to wrap text on 3 lines
Button1.AdjustsFontSizeToFitWidthXC //Adjusts the font size to fit all text

Add some styling and you get this:

Screenshot 2022-01-26 at 13.24.49

I had that same setup with labels and pictures inside a canvas, but what drove me away from
the canvas was…

  1. Jeremie’s recommendation to use buttons instead and
  2. the Canvas reacts much more sensitive on a tap than a button. I had lots of feedback that there’s too many accidental taps on the Canvases.
  3. Also, as soon as the user puts the finger on the Canvas, it will fire the PointerDown and PointerUp events, even if you move the finger outside of the Canvas before releasing it. The PointerUp will fire anyway. With a button, the Pressed fires only if you tap it and keep your finger on the button until releasing it.

I’m attaching a screenshot where you see what you can do with a button and Jeremie’s iOSDesignExtensions. The players are all custom table cells with white buttons, labels and pictures on it.

Of course you could do the same with transparent buttons, just like a canvas.

1 Like

In order to adapt the height of a multi-line button, I will set the button caption to “”, add a label in front of the button with a min height constraint where the text will go and then add a height constraint to the button being equal to the label’s height.

Thanks for the replies. I already have iOSDesignExtensions in the project and will try out the buttons as my answer choices. The accidental touch on a choice is not a big deal. The user has to still hit Submit after they make their selection and can change the answer before hitting Submit

Chris - That’s quite an awesome looking app! Well done on the design!

I started playing with the iOSDesignExtensions, and I love it.

Yet, I can’t get the text alignment on a button to work.
Whatever I do, all text in the button stays centered.

Also, is it possible to vertically align the text in a MobileLabel?
My workaround is using a MobileTextArea and set it to ReadOnly, and make the background transparent.

That’s strange, it used to work. But I just tried and it doesn’t anymore.

I will update iOSDesignExtensions right now with a new method.

You will then be able to left/right/leading/trailing align the Button’s caption like this:

Button1.setContentHorizontalAlignmentXC(ControlExtensionsXC.UIControlContentHorizontalAlignment.Left) //or use .Leading if your app supports Right-to-left languages

Button1.SetButtonInsetsXC(ExtensionsXC.UIEdgeInsetMake(0, 10, 0, 0)) //Add 10pt padding to the left

–

EDIT: It actually never worked. Only the following worked for multi-line captions

Me.SetTextAlignmentXC(ControlExtensionsXC.NSTextAlignment.center)
1 Like

Maybe. I’ll have to look into it.

Your workaround is a valid way of doing it. Many apps use that technique to format text.

1 Like

Thank you so much! You’re awesome!

I downloaded your module, and updated my app with your new module.
While testing it in your test app, it works.

But when I apply the alignment to my button, nothing changes. I can’t even set the background color of my button. And the caption remains centered, even though I set it to Left (or leading) :thinking:

The button I want to change is inside a MobileTableCustomCell.
When I add a button to a MobileScreen, your extension works. Is there any reason why a button in a Custom Cell cannot be altered?

Do you call the button modifications in the button’s opening event? I have stopped using the opening events when the button is inside a custom table cell because it never works for me.

1 Like

Opening events in MobileTableCustomCell do not work.

Add a method to your MobileTableCustomCell such as Setup or SetUI.

Each time the custom cell is created, call:

TheCellClassName(cell.control).Setup
1 Like

Yes, I did. I changed my code a bit. And now it works like a charm. Thanks for point that out!

I changed my code. I have a property that holds a record of a database. I use a class of my custom database framework, I made years ago.
When the property is being set, it populates labels, fields, etc in my CustomCell. And that is where I set the button style. And now it works. Thanks!

Could it be a bug in Xojo, that the Open Event is not handled in the way we expect it to be?

Yes definitely a bug.
https://tracker.xojo.com/xojoinc/xojo/-/issues/70844