Bounds vrs Frame Center etc in ui
A few last points. I know it works out if you use layout . But that's the only way.
Unless you use layout callback, it can never work. I suspect this is the way it was crafted in the first place. I don't even need to understand Python to know I don't know what I don't know.
If the end point for the view size/bounds whatever is present, then without using layout the only logical way is to position all your objects after present is called.
I have never seen any sample code here like that. I am happy to do it that way if that's the way.
But again, Center is only one issue. And yes, I understand I could have just misunderstood it. But I think the root problem is that the final size of your ui.View is unknown. Even if it was, next year, a new device and @omz modifies his algorithm, back to square one
ccc last edited by ccc
We have no idea what @omz display algorithm will size the eventual ui.View at.
We do when
From the ui docs...
def layout(self): # This will be called when a view is resized. You should typically set the # frames of the view's subviews here, if your layout requirements cannot # be fulfilled with the standard auto-resizing (flex) attribute. pass
As I have written before, I used to avoid ui.View.layout() but now I embrace it. Those fleeting moments of sanity are a beautiful thing!
@ccc , really? Look, I understand if you write anything close to important you need to use layout. For orientation etc.. Doesn't mean the rest of it should be broken.
Again, I don't understand why my simple premise is not embrassed. This can all work. Just need to be able to call something like bounds = present.preflight('sheet')
Again, I don't get it... It's just so logical
For sheet, you know exactly what size your view will be (unless you make it too large). You can use layout() as a "preflight", or to call a preflight function, it is called whenever something is resized, use a flag if you only want it to run once, though be careful that you don't change the frame after creation. Or, use the flex attributes. For instance if you set btn.flex='lrtb' your original code would keep the button centered in all view types.
If you want to make ui's that are orientation friendly, you are forced to use layout or flex anyway, anything you hard code based in some preflight will be broken anyway once you rotate the device.
How would preflight know how you plan in presenting your view, and in what orientation?
It would be nice if the title bar sizes for the various modes and devices was documented, though more for testing what your ui looks like in a small device.
@JonB , well preflight could return a tuple for horizontal and vertical frames or bounds or whatever they are.
Again, I will reiterate, we can't know what we don't know. But Pytonista knows what is going to do. We just need to be able to ask it without it doing anything other than returning the results of the sizes of what it is going to based on what we are passing to it.
Apple Watch is an example.
I would put money on the way to resolve this issue is a preflight method. All the code is there already. It also makes omz more device independent from the api.
JonB last edited by JonB
Here you go. I tested on iPad, but you should test this on iPhones, I believe the landscape title bar is only 32 instead of 64, as mentioned in the referenced thread.
For sheet, this function returns the max size... if you exceed width or height, presenting as sheet will result in a 576 square. Maybe this function should take a desired frame as an input, returning final frame size. Also, technically, sheet doesn't work on iPhones, so perhaps that should be sensed and accounted for here. These improvements are left as an exercise to the reader :)
Again, I can think of only a very few cases where this cannot be done, and done better with either flex or layout. I will admit flex can be annoying, as it takes many layered views to do anything complex. Frankly, for simple throwaways, I usually present first, the 50 msec of dark while the view sets up is not a concern.
At one time I started working on a set of Layout Managers, see my uicomponents git, BoxLayout and uicontainers (which has a FlowLayout container), that would make this easier. the major stumbling block was the lack of min/max component size attributes. Now that we can set arbitrary attributes, it would be possible to implement real layout managers, that let you specify desired size, and min/max sizes of components.
(edit fixed a few typos and slight mod to the code)
import objc_util def get_presented_size(mode,hide_title_bar=False): ''' see https://forum.omz-software.com/topic/1618/any-ios-device/7''' f=objc_util.ObjCClass('UIApplication').sharedApplication().keyWindow().frame() sz= ui.Size(f.size.width,f.size.height) if sz<=320: status_height=0 title_height=32 else: status_height=20 title_height=44 if mode=='sheet': maxsize=min( sz-(0,title_height*(not hide_title_bar))) return ui.Size(maxsize,maxsize) elif mode=='panel': return sz-(0,status_height+(title_height*(not hide_title_bar))) elif mode=='fullscreen': return sz-(0,(title_height*(not hide_title_bar)))
@JonB , nice. I am going to name it preflight 😀😱💋
But thanks, its really nice.
My mind is still doing back flips, as this conversation always confuses the hell out of me. Just when I think I have worked it out, I do something that undoes my thinking...
But, the mode 'sheet' does not work correctly on an iPad Pro.
If you do same as the others,
return sz-(0,status_height+(title_height*(not hide_title_bar))) It works. But I am sure done for a reason the way it is so I am sure something else must be done.
It would be nice if you could also pass your intended frame or (w/h) to the function so you are not getting the maximums but the real size. That will be presented.
Of course this can get effected if you ask for a size not support on a device.
But I don't trust my self, to tinker with code.
Feel free to use or modify this, as long as you don't call it preflight :)
I'll have to try this again (admittedly I didn't test sheet in this final form). For the 768x1024 devices, this should return something like 768x768 when title bar is hidden, and 704x704 when it is not. From what I can deduce, sheet present modes have to work in both orientations, so your size is limited to the narrowest dimension of your screen, minus the title bar height (if title bar is shown).
Can you run my little script in https://forum.omz-software.com/topic/1618/any-ios-device/7 and tell me what you get in both orientations on your Ipad Pro? What did this script return? I suspect my logic could be off for some of the newer iPhones as well.
@JonB , lol...deal
('full_screen', True) 0 (0.00, 0.00, 1024.00, 1366.00)
('full_screen', False) 0 (0.00, 64.00, 1024.00, 1302.00)
('panel', True) 0 (0.00, 0.00, 1024.00, 1270.00)
('panel', False) 0 (0.00, 0.00, 1024.00, 1270.00)
Will send iPhone 6s soon
('full_screen', True) 0 (0.00, 0.00, 375.00, 667.00)
('full_screen', False) 0 (0.00, 64.00, 375.00, 603.00)
('panel', True) 0 (0.00, 0.00, 375.00, 579.00)
('panel', False) 0 (0.00, 0.00, 375.00, 579.00)
@JonB , do you need it done on iPad Air 2?
I am pretty sure I concede. I thought if we knew the sizes before present we could force ui.View to do the right thing. I have been testing and testing...
Without present being called you just can't get it right. Well from what I can see.
You can say layout, but it does not matter. Layout is being called after present. So same thing.
So I concede, I am wrong. The magic preflight fixes nothing. It's in the Pythonista code.
I get it now.
Either you have a custom class and support layout, or if you are making a view ad hoc to display, to be 100% you need to present it first.
Not ideal, but that is the way it is I guess.
I can't remember the old Mac toolbox api , but we had something like suspend drawing. But basically, you could turn off any screen rendering. But the so called 'view'/window was already created.
It was like a transaction (I think I mentioned it last night)
But was something like
Do your stuff
But It could be
v.present('sheet', suspend_upates = True)
v.resume_updates = True
Or v.suspend_updates = false # causes a redraw
Anyway something like that. Just an illustration
Ui.animate does that. Anything you put inside the function sent to ui.animate will happen at once. Just set the time to 0 to have it happen instantly. IIRC anything happening in the main ui thread also will appear instantly(so inside a non backgrounded button action f
Is your view initting so complicated that you can see the subviews getting added, etc?or example), or perhaps using on_main_thread.
@JonB , no is about delays etc. I see you said before that you don't care about 50ms black when you present your view then build it. What I have done on the ipad pro , there is no delay. It's so quick. I am just being anal.
I would just like it to happen the right way. I am sure in your job you are the same way when you are doing Java or whatever languages you work in.
I know people are a lot more fee spirited when it comes to Python (hippies 😳) I don't know why. It's very powerful. But maybe omz has show the real power of Python, by providing a great ui as well as tools.
I know good Python programmers are often in the top 3 or 4 best paid programmers behind the c variations and Java. Not that I am looking for a job. But I am sure 95++ % is backend stuff. Ok, I even lost my train of thought here.
I just like to see things right. I know I am not the best judge of that with my lack of experience in Python. But it's a journey. Have to keep questioning things. Same way I learnt eons ago.
But about ui.animate. I can't see that would change anything for adhoc ui.Views() first you need a custom class. Secondly it will be presented already
You can set view.hidden =True, which sort of does what you are asking, at least for initial setup. You will see the empty view while things are prepared, but you could add an ActivityIndicator
Also, ui.delay (not animate like i thought) will cause everything to happen at once -- the ui is not updated until the delayed function exits.
See this example, where I show 4 methods. For a really slow setup function, you see the subviews get added one by one using the basic method (present, then setup the view) which is undesirable. Using ui.delay is nice, and you can show an activity indicator while the view sets up. You can use hidden on your root view, though it shows black while setting up. So, you can have a subview of root as your main view, and just hide the subview. This method also lets you add an activity indicator. This is probably my favorite approach. You could even write it as a context manager.