That is one beautiful layout! The Pane looks great.
Talking about UI layout and the science behind all this, I am reading a new book called :
“Designing with the Mind in Mind: Simple Guide to Understanding User Interface Design Guidelines”
Very nice book focusing on how we observe, process and remember etc. Great book written by a college prof based on his lectures. He presents the human psychology concepts that affect how people work with our UI in a simple to follow language and lots of examples. Most recommended read.
Also while on the topic… Have you seen the 3.2B purchase of Nest by Google? There are many smoke detector and thermostat makers in the market but none look as neat as the Nest products or have the winning UI elements they managed to bring to these consumer tech elements that hung on our walls for the last 40 years. So you get your UI and functionality right and make it look neat you have a winning formula. Congratulations to Nest for getting it right.
I guess a lot depends on what your app is using for UI elements. I looked at a couple of business apps to see what they are doing. In many cases, you have a tableView that is display.contentHeight - navBar.height - tabBar.height (assuming you were using the Ultimate config.lua modernined version). Then your rows would fill the height. With Graphics 2.0, being self masking you can make this fit exactly.
Now looking at a couple of other screens, they might have scrollViews that fill the area. In looking at the SquareUp app (lets you take credit cards), one table has a numeric keypad for entering numbers. I’m speculating, they are calculating the height of the buttons based on the available real estate. In this screen, there is a bar across the top, below that is a bar that shows the amount your going to charge, then another bar where you can enter a note to be attached to the charge, then the 4 row, 3 column set of numeric keypad buttons.
If I were building this relative to any size device, I would determine the height of each button by doing: buttonHeight = (display.contentHeight - navBar.height - tabBar.height - noteRow.height - amountRow.height) / 4.
That way the keypad buttons would adjust height based on the screen size. Looking at the United Airline app, they have a button bar that’s a scrolling button bar just below the top navBar. On my iPhone 5 it shows 4 buttons and I can scroll to see more (bad part of their UI design, they don’t show you clues that you can scroll, may have been in their help that I skipped!). On the iPad, they show about 6 buttons before you have to start scrolling.
Then there is a photo of a plane that seems to fill down from the top and they have a 3 row set of actions at the bottom, a bar with a sign in, and then two bars with two buttons in a 2x2 grid though the buttons height varies. on the iPad the buttons are stretched to fill the screen so they look shorter and narrower though they are in effect really the same height. In this case, you have a bar anchored to the top and a block anchored to the bottom and the buttons on the bottom would be display.contentWidth / 2 in width and positioned display.contentCenterX - half of the button width and display.contentCenterX + half the button width. Since the button width’s change based on screen width, they will be positioned regardless of the screen. Most of the individual screens are either a tableView or scrollView that fits the display.
I would suggest studying some business apps that have features you like and see how things are anchored to the top or bottom of the screen and scrolling objects like tableViews and scrollViews are used to fill the in between.
Rob
Very interested in this topic. One thing I have to offer is that scaling of UI elements in business apps is often a no-no. Look at the size of typical elements across platforms and you will typically notice that they remain same. In other words, the height of your tabBar, rowHeight in your tableView etc is comparable. Designing for iPhone and then letting Corona SDK up-scale things for iPad works robustly but the resulting UI does not make use of the extra screen real estate on the tablet and creates very large & chunky UI elements. Something to think about.
Interesting points. Does it not make sense to separate the issue of scaling individual UI elements from the discussion of how to place them in reasonable (but dynamic) relation to each other? In other words (initially, unless I’m missing something), I’m much more concerned with dynamically determining the x,y (former top, left) than I am with calculating the height/width…once the proper relative position is established, the scaling would seem self-evident? What’s the design pattern for calculating the CORRECT x & y as my renderUI() function proceeds to add lots of (arbitrary) objects down the screen…that “percent from top” formula is the best I’ve come up with, but I’ve not heard it discussed and tend to distrust beginner innovation
Perhaps I’m thinking about this all wrong, but as I said, I’m a bit new to the UI game…
Thanks for your thoughts!
Dewey
In most cases of tableViews or scrollViews, you just add things starting at the top and draw them down the page. If there is more than can show on the screen, then the view will let the user scroll them into view when needed. In this case, your UI elements will have the same height regardless of the shape of the device. Since Corona SDK uses a defined content area, you don’t have to worry about the scaling.
But if you look at the United app I was referring to earlier, you will see that their buttons are anchored at the bottom.
https://itunes.apple.com/us/app/united-airlines/id449945214?mt=8
So between putting things at the top and filling down, or putting things at the bottom and filling up as long as you don’t put too much stuff that won’t fit on the shortest screen you support, you won’t have a problem. Then when you have more than fits, put it in some scrolling view.
Rob
It’s the “filling down” pattern that I’m trying to get a “best practice” for…
Lets assume I have 3 objects I want evenly spaced on the viewable (ignore scrolling for a moment) area of my screen.
Sounds like you are saying to do something roughly like this:
local objSpacing = (display.contentHeight - (titleHeight + navHeight)) / 3 – since there will be 3 objects
obj1.y = titleHeight + 30 – 30 is just some guestimate so the 1st obj is not too close to the titleBar
obj2.y = obj1.y + objSpacing
obj3.y = obj2.y + objSpacing
If that’s what you’re suggesting, then I completely understand that part…what I don’t get is how do I make that 30 (or titleHeight, or navHeight or any other constant in my code) dynamic based on the many device screens I might encounter. For example, 30 px might be fine on an iPhone3, but on an iPhone 5, that 30 should be 2x or 3x (ie 60 or 90) for the same visual aesthetic. And navHeight will be 44 on one device and 88 on another…has anyone written a std func to derive these constants??
Does this clarify my question?? Or am I missing something fundamental??
Thanks for hanging in there with me on this…
Hi dg, There are some Ui design patterns here http://time-tripper.com/uipatterns/Liquid_Layout and the book is also very nice. Most visual framework do have panels that implement the different visual patterns for placing and spacing the UI elements automatically on a panel. This is something I plan on working soon and we can certainly all join forces. Additionally, the storyboard framework will need to be expanded to support different scenes for tablets vs phones
You know your contentHeight and how much any app Chrome takes up (navBar + tabBar). If you want three items to fill the remaining area, you want an item at 25%, 50% and 75%. There are of course several ways to accomplish the calculations. One way is center out thinking. The middle item will be at display.contentCenterY. The other two need to be drawn half way up and down the remaining content area:
offset = (display.contentHeight - tabBar.height - navBar.height ) / 4
obj1.y = display.contentCenterY - offset
obj2.y = display.contentCenterY
obj3.y = display.contentCenterY + offset
Another way is just load them down the page:
obj1.y = offset + navBar.height
obj2.y = offset * 2 + navBar.height
obj3.y = offset * 3 + navBar.height
If you have a dozen things 60px each and you want to load up a scrollView:
for i = 1, #obj do
obj[i].y = (i - 1) * 60 + navBar.height
end
or something like that.
Rob
[quote name=“Rob Miracle” post=“224000” timestamp=“1388972962”] In this case, your UI elements will have the same height regardless of the shape of the device. Since Corona SDK uses a defined content area, you don’t have to worry about the scaling.[/quote] Close but no cigar. Don’t want to pull thread towards scale as DG wants to explore spacing first but remember that not all pixels are created equal. Some are tall, some are short… Atanas, great link. Thanks for that.
Yes…@atanas…thats a really great resource
Rob,
Thanks for your patience & help.
You said:
You know your contentHeight and how much any app Chrome takes up (navBar + tabBar)
But that’s not necessarily true…I do know the contentHeight, but depending upon the device, as Ksan said, my navBar might be 44px (on a 480h screen), or it might (should) be 88px (960 screen) or even larger depending upon the device. Correct?? So how do I derive those constants in the first place?? Perhaps I’m looking at the WRONG (not updated) ultimate config.lua, but it does not always assume 320x480 and therefore, those constants will be dynamic (at least I think they will).
The obvious answer seems to be to take the ratio of:
local deviceNavBarHeight = contentHeight/480 * navBarHeightAt480 – rounded to int
Is that all there is to it??
Oh, and you are getting really close to my intended topic of this thread when you mention “center out thinking”.
I’d like to see an article explaining the rational (pro’s & con’s) of chosing between:
- center out thinking
- anchored to top
- anchored to bottom
- etc – any other patterns that are “recommended” or best practices according to CL
Dg, You are probably in uncharted territory, please keep us updated as you move forward. Android has linear and relative layouts and you could find their default values for padding and margins, although I do not have a direct link this could help http://developer.android.com/guide/topics/ui/declaring-layout.html.I do remember java had some containers implementing layout patterns, and the VCL implemented constraint based Ui layouts. Layouts are a high priority item to have for business apps, but first things first - the widget library is still quite buggy and missing some basic features
Yeah, I’ve got some experience with GWT (Java) and it’s panels and layouts are fabulous…it’s that sort of pattern I’m looking to replicate here. Thanks for the links!
I’m all with you on this. I have implemented a widget.newPanel but so far has only the visual part, was hoping to get to the UI layouts at some stage. Would be glad to share it if you would like. Also subclassing storyboard to return a scene-“tablet” so user can have two different forms/scenes for smaller devices and bigger screens
Yes, I’d love to look at your widget.newPanel
Have you put it in a git repo??
If so, I’d be happy to collaborate with you to improve it…
I uploaded it on my github at https://github.com/atanasster/framework-widget/tree/master/widgetLibrary .
Unfortunately I have no sample app to show the capabilities, but here is a screenshot (its the bottom panel that has the buttons and text aligned in two columns). For the time being, I am doing the text and buttons layout “manually”
But that’s not necessarily true…I do know the contentHeight, but depending upon the device, as Ksan said, my navBar might be 44px (on a 480h screen), or it might (should) be 88px (960 screen) or even larger depending upon the device. Correct??
No, that’s not correct. Let’s for a minute assume that all screens are 50% taller than they are wide (iPhone 3/4 shape) (Humor me). You have a 320x480 screen, a 640x960 screen and an 1280x1920 screen. If your config.lua has your width set to 320 and height to 480, you are defining what are known as “Points”, not “Pixels”. Your content area will be 320 points wide and 480 points high. On that super high-res screen, each point is 4 pixels. So lets say you draw a square at x = 100 in Corona. On the 320x480 screen it will be 100 pixels from the left. On the 640x960 screen it will be 200 pixels from the left. On the high res screen it will be 400pixels from the left. You only use 100 and Corona does the rest.
When we put our numbers in our config.lua we are defining our content area and the values we want to use for that area. It has ZERO relationship to screen pixels. So in the Modernizing config.lua we recommend using an area based on 800 x 1200. We made those numbers up but the idea was to break away from you thinking about pixels and just think about a grid you can use. Corona will do the math to map the points to pixels for you.
Now let’s factor in Aspect Ratio. I have never seen a device with a non square pixel on the market. 99.9% of the devices out there have square pixels… if you’re using “letterbox” or “zoomEven” scaling. If you use “zoomStretch” then you can get into a situation where the points are not square (the device pixels are still square). (NOTE: Devices may use funky shaped physical pixels, but drawing pixels renders square effective pixels… I know some camera’s that use non-square photosites but the output JPEG has square pixels for instance). Any way back to aspect ratio.
Apple for the longest time only had one: Their screens were 1.5 X higher than they were wide. The IPad came along and it’s 1.33 X higher than they are wide. Many android devices have chosen to go more towards a high-def TV in shape, but it’s very inconsistent. This is what creates misery among app developers.
To make life easy, we developed the ultimate config.lua to give you a content area that is calculated based on the device’s shape, but around a consistent “size”. So staring with 800x1200 for the 1.5 x 1 screens, for an iPad, the content area becomes 900 x 1200. For the iPhone 5/5s it becomes 800x1420. The idea here is that you can create one graphic that will work regardless if its on a wide iPad or a tall iPhone 5. You still have to provide @2x images for the higher resolution screens, but all your math is around a fixed area.
Does that help?
Rob
Yes, it helps a lot, and it also seems to be mixing two different issues.
In your first paragraph, you said:
Your content area will be 320 points wide and 480 points high
And so in that case, your example of square.x being at 100, 200 and 400 px respectively works fine
Which is good because, in this scenario, proportional distance between all other objects will be preserved…
All well and good…I’m totally with you so far!
but in your “ultimate config” those math-calcs cause the 320 x 480 (or whatever “base-content-area”) to change dynamically depending upon which device you are on.
So yes you can “create one graphic that will work regardless if its on…abc”
In that case, the 100, 200, 400 pattern is broken…that object will not (unless I still misunderstand something) continue to have a proportionate distance from screen edge and other objects…will it??
And finally, can you validate or dispute my example math above so that I can determine (based on which device I’m on) how tall my navBar should/will be?? Or are you just saying I should create the navBar graphic, let Corona scale it, and then read it’s final height value to see what it is, and then use it elsewhere? I hope not because there are TONS of problems with that approach…
Thanks again for your help and patience!
Not really. Lets leave the iPad (and some phones with physical keyboards) out of it at the moment. Our base aspect ratio is 1.5:1. This is the iPhone 3/4 standard that you get on a 320 x 480 screen (480 / 320 = 1.5). All other phones and tablets have a 1.5:1 aspect ratio or higher. Some Android devices based on the TV 16:9 are 1.777778:1. On all these devices using the Ultimate config.lua modernized version, with a width of 320, will have a 320 point wide content area. The actual number of pixels those 320 points represents will vary based on the device, but because Corona is managing that math for you, you only have to worry about your devices being 320 points wide. Period. So square.x = 100 will be 100 points from the left. You don’t worry about pixels. Forget pixels even exist.
Now because screens have different aspect ratios, the display.contentHeight will return a number that will be different depending on the device. On an iPhone 3/4, it will return 480. On an iPhone 5/5s, it will return 568. On an 7" Kindle Fire HDX it will return 512.
Regardless of the device, if you draw your square at a y of 100 (square.y = 100), it’s going to be 100 points down from the top. What you cant do is say, I want that box to be 100 points from the bottom and say square.y = 380 (using a 480 base content area) because 380 may not be 100 points from the bottom. To get around your app not knowing what device it’s on. You have to do a little math to figure this out. square.y = display.contentHeight - 100 will on every device position the square 100 points from the bottom.
With me so far?
Lets throw in the iPad now (and those odd old keyboard phones). We could have chosen to leave off the calculation for devices with a wider aspect ratio and had the iPad be 320 points to be consistant, but if we did that, the display.contentHeight would have been 427 pixels and it would have created confusion because now the screen would be too short and your graphics would have looked a bit larger on the iPad. It seemed to me that it would be better to keep the iPad at 480 points high and make the 320 into 360 points. To visualize this if you have an iPad take your iPad and hold it at arms length. Then take your phone and hold it (both portrait orientation) at a distance where the phone’s height matches the iPad height. See how there is extra iPad on either side of the phone? That’s what I tried to accomplish with this. On the iPad the extra space is on the sides, where on the tall phones the extra space is on the top and bottom.
Doing this using the 320 and 480 numbers, the iPad mathematically works out to a 360 x 480 point device. Now if you draw square.x at 100, it’s going to be 100 points from the left edge, just like it will be on the other devices. But unlike the other devices, it will be further away from the right edge. But this is okay. If your display object needs to be a fixed distance from the left, you just set x = that distance. The fun comes in now if you want something a fixed distance from the right edge you have to use math to figure it it. You would do square.x = display.contentWidth - 100 to make sure it’s 100 points from the right edge.
I believe I follow you.
You are saying that on all devices with the std (1.5:1) aspect ratio, because of the ultimate config.lua, that display.contentWidth will ALWAYS return 320. On devices with higher AR, it might return up to 360.
For display.contentHeight, all (currently known) devices will return 480 or greater up to 568
and so long as “center”, “right” or “bottom” anchored calcs always use the dynamic display.contentXXX values, then the UI will look right on all devices.
And for “left anchored” objects, (even tho the 100, 200, 400 pattern may vary on wider devices) they will stay proportionally distant from each other, but just not proportionally distant from the right edge…
Is that all correct?
If so, can you translate those numbers (320-360 and 480-568) into their equivalent min/max for the 800 x 1200…for example:
width range will be: 800 up to _____
height range will be: 1200 up to _____
So then, if I’m building my own navBar (rather than loading png via newImageRect() ) I can code such that my navBar is ALWAYS 44 px high (assuming 480 instead of 1200) regardless of the device…I won’t need any calcs to figure out variable element sizes!!
Still correct??
Thank you so much!