Setting Control Widths in iOS

OK. Here’s where I am getting hung up. Let’s start with a basic control that I have created. I have the canvas for one of the controls in the IDE on the view. It’s height is 532.

In the open event of the control in desktop I have the following code:

self.Width = ScaleFactor*Picture_SliderWidth

Picture_SliderWidth above is a computed property with this in the getter:.

If UseLEDBar Then Return 165 Else Return 107 End If

ScaleFactor is a computed property with the code below in the Get method:

Return Self.Height/463

Now this works swimmingly well.

In IOS, I have the following:

[code]MyWidth = New iOSLayoutConstraint(Self, _
iOSLayoutConstraint.AttributeTypes.Width, _
iOSLayoutConstraint.RelationTypes.Equal, _
Nil, _
iOSLayoutConstraint.AttributeTypes.None, _
1,mWidth,1000)

MyWidth.Offset = ScaleFactor*Picture_SliderWidth

Self.AddConstraint(MyWidth)[/code]

The computed properties are the same in iOS as in desktop. But for some reason the code just wasn’t working. Then I discovered that in the ScaleFactor computed property, self.height is ALWAYS being reported as 100. It’s not reporting the height of the control instantiated on the view, but some default height.

This is why none of my stuff is working.

Why is the height of the control not being reported properly. Note, all this code is in a custom control subclass of iOSCanvas.

it will probably work very well in desktop since dekstop doesnt use autolayout

on iOS when are you checking the height ?
immediately after adding the constraint ?

and does your canvas already have a width constraint by default and when you add this one now it has 2 ?

I’m checking the height in the ScaleFactor computed property:

Return self.height/463

I create the constraint. I set the offset based on the height and then add it to the control just as shown above.

This is in the open event of the custom control subclass.

I removed the width constraint on the controls in the IDE.

The problem is height is always returning as 100. But the control instance in the IDE is 532.

Yep. I can confirm that the height of a custom subclass is not properly reported in the open event of that subclass. I’ll be filing a bug report. I have an example project.

Here’s the bug report: <https://xojo.com/issue/59073>

And it’s not just custom controls. EVERY iOSControl reports an incorrect height value in its open event. The other boundary properties of the control are properly reported incorrectly as well - I just haven’t tested that yet.

The open event is where you can define auto-layout constraints.

The correct boundaries will be available in the Activate event, after the constraints are applied.

[quote=474232:@Jeremie Leroy]The open event is where you define auto-layout constraints.

The correct boundaries will be available in the Activate event, after the constraints are applied.[/quote]

That’s incredibly dumb. A control is defined by how I place it in the IDE. And I have to fight auto layout just placing controls in the IDE! I had a text field in my example project go 1024 pixels wide for some unknown reason due to auto layout.

That’s just really bad.

How is Xojo EVER going to have true code portability between platforms with stuff like this.

And in my example program I tried setting the value of the text property in a text field and nothing shows up.

I suppose now you are going to tell me that using:

TextField1.Text = SomeTextValue

Is not the right way to do it?

[quote=474232:@Jeremie Leroy]The open event is where you define auto-layout constraints.

The correct boundaries will be available in the Activate event, after the constraints are applied.[/quote]

And I am sorry, there is no Activate event for a control.

So it’s impossible to size an manipulate a control in the Open event. 100% impossible because the proper values are not reported. That is a bug. Plain and simple. The fact that I now have to stick in a timer and call that timer in the open event to run my code is a kluge. Please don’t defend Xojo’s bugs and horrible practices.

I am not defending anyone here. But I know how some things work at the system level on iOS.
The view hasn’t layed-out the constraints when the Open event fires. It only does so afterwards.

True code portability, in native apps, when it comes to the UI is impossible. Desktop/Web/iOS controls don’t behave the same. They look similar, have similar features but are different in their core.

[quote=474236:@Jeremie Leroy]I am not defending anyone here. But I know how some things work at the system level on iOS.
The view hasn’t layed-out the constraints when the Open event fires. It only does so afterwards.

True code portability, in native apps, when it comes to the UI is impossible. Desktop/Web/iOS controls don’t behave the same. They look similar, have similar features but are different in their core.[/quote]

Then Xojo should have created an “ApplyConstraints” event where this is done and not in open. And actually the constraints are applied AFTER open fires.

And I can’t add a timer and do all my stuff because the paint event fires first and it’s dependent on things done in open!

Obviously, Xojo applies their constraints AFTER the open event is finished firing. But when? I tried adding a constructor method. And then after Super.Constructor is called, I call my own “open” method. Once the Super.Constructor method is finished, EVERYTHING should be set for the control because that is where the Open event for the control is raised, etc. But even after Super.Constructor, my control is STILL reporting a default height value. This is applied somewhere else.

So like I say, it is IMPOSSIBLE to properly size and adjust controls dynamically based on control properties in Xojo’s implementation of iOS. That’s just wrong. It’s a poor design, I don’t care what is claimed about iOS under the hood.

I got bit by this a few years back: when you have a constructor even if you set values for variables, they will be overwritten based on what is set in the inspector after The constructor happens. I made setup methods and called them 5ms after the open event to be safe using CallLater

Yeah, I created a timer and did this. I forgot about using CallLater. Good idea. It’s poor design though on Xojo’s part.

The constructor is NEVER a good place to put initialization of properties for controls

Basically in all cases a control is added to a view with code the IDE generates that is much like

       // your constructor code gets called here !
      dim c as Control = new <whatever your control is called> 

      // and here your properties get overwritten with the values from the inspector
      c.setAllpropertiesFromInspector 

        view.addcontrol c

And this is where inspector behaviour comes into play - and why its needed
If you expose that and set the values in the inspector then you can have per instance values that will get set to whatever value is set in the inspector (instead of trying to initialize it in the constructor)
I’ve mentioned this about a million times on these forums , the Xojo blog and my own where I actually posted some code that would make it possible to override some of this behaviour using computed properties since you could know if you were being set from the inspector or not - see my post about Making a constructor sometimes illegal to call

Norman,

A couple of things:

1.) I have not been trying to initialize things in the constructor but in the Open event. Of course, I guess you could say that is “inside” the constructor since the open event is raised by the Super.Constructor method. But one should be able to initialize code in an open event. Silly that you can’t.

2.) Anything you put AFTER Super.Constructor in a Constructor method should be the LAST things called. Everything else that is set (ie in your example above: c.setAllPropertiesFromInspector) should be called from the Super.Constructor method. If it doesn’t work like that, then there’s no real way for a user to properly control how code executes when they are creating a control. That is the problem here in iOS. I have to rely on a timer and hope that after x clock cycles when my timer executes, that everything is the way I want it and my operations won’t fail. I’ve already had to add code to my paint event to bail if my constraint is NIL because I can’t create my constraint and add it to the control until the timer fires. It’s just crazy nuts.

[quote=474268:@Jon Ogden]Norman,

A couple of things:

1.) I have not been trying to initialize things in the constructor but in the Open event. Of course, I guess you could say that is “inside” the constructor since the open event is raised by the Super.Constructor method. But one should be able to initialize code in an open event. Silly that you can’t.
[/quote]
On iOS OPEN is still a tad early - unfortunately - as this happens when the control is created but BEFORE iOS has had a chance to do all its layout and resizing passes
unfortunately there isnt a much better spot in the ios framework to hook this event in to :frowning:

[quote=474268:@Jon Ogden]
2.) Anything you put AFTER Super.Constructor in a Constructor method should be the LAST things called. Everything else that is set (ie in your example above: c.setAllPropertiesFromInspector) should be called from the Super.Constructor method. If it doesn’t work like that, then there’s no real way for a user to properly control how code executes when they are creating a control. That is the problem here in iOS. I have to rely on a timer and hope that after x clock cycles when my timer executes, that everything is the way I want it and my operations won’t fail. I’ve already had to add code to my paint event to bail if my constraint is NIL because I can’t create my constraint and add it to the control until the timer fires. It’s just crazy nuts.[/quote]

you should not try to set intrinsic property types in a CONTROL constructor (integers, colors, strings, doubles, booleans etc)
desktop or ios are both this way (web might be too but dont quote me on that)
they will all get set using the pattern I stated above
object types are safe to be created though as they cannot be exposed in the inspector behaviour

this has always been true

for ios in particular a LayoutDone event would be really useful as at that point all the controls and constraints have been determined and then you could do the work you’re describing
And a new method to call to ReLayout would be nice so you can, in LayoutDone change / alter constraints accordingly and then cause a relayout of everything
You’d have to be careful to NOT cause an infinite LayoutDone / Relayout cycle but that would make iOS much nicer to work with

This blog describes how iOS does this quite nicely (Apple has a similar presentation somewhere)
https://www.vadimbulavin.com/view-auto-layout-life-cycle/

Not sure if someone cared to hook into iOS to catch a views “viewDidLayoutSubviews” which would be LayoutDone
And a hook into the views “updateViewConstraints” could be ReLayout

just a thought

[quote=474272:@Norman Palardy]On iOS OPEN is still a tad early - unfortunately - as this happens when the control is created but BEFORE iOS has had a chance to do all its layout and resizing passes
unfortunately there isnt a much better spot in the ios framework to hook this event in to :frowning:

[/quote]

So it seems like its similar to open then in Web. At least web has a “shown” event. IOS then should as well.

Then you are basically saying then that intrinsic property types shouldn’t ever be set in the Open even of the item either. The open is called as part of Super.Constructor. Technically any code placed after Super.Constructor would run after the open event.

The open event isnt called as part of a Constructor method you add to a control