I have a minimal app with basic views, texts, images and buttons in react native. The problem I am facing, is in managing layout for multiple devices.
Any suggestions on how can I maintain a same (proportionate) view & font size for multiple devices (multiple screen resolutions and screen sizes).
View:
For example, if I want a list view of items, where each row has one image on the left, some text in between and 2 buttons on the right (one below the other i.e. flex-direction: 'column'), how can I have this look good on a 5" device as well as a 10" device?
Also, consider the buttons have some borderRadius, if we are scaling the buttons, the borderRadius value will also need to be increased. How do I achieve this?
Fonts:
I have tried using PixelRatio.getFontScale() but this scales the font depending on only the resolution (from what I understood). Using this, makes a font of size 12 look bigger on a 5" device with higher resolution and smaller on a 10" device with lower resolution. So how can I manage font sizes for the react native app?
In addition to the PixelRatio.getFontScale() you can also use the Dimensions API to get the height and width of the window, and scale your components relative to that. I have built an app that runs on both iOS and android and use this to scale width's + heights.
Checkout the link Dimensions # Facebook - React Native
ie.
Dimensions.get('window').width;
Dimensions.get('window').height;
Will return you the size of the window
How about this library? react-native-scaled-layout
You can use scaled dimensions for your layout margin, height, padding ...
(36).scaled() /* or */ (36).d()
(36).widthScaled() /* or */ (36).w()
(36).heightScaled() /* or */ (36).h()
(24).fontScaled() /* or */ (24).f()
style={{
width: (100).w(),
height: (210).h() + safeAreaBottom,
borderRadius: (16).d(),
justifyContent: 'center',
paddingBottom: safeAreaBottom + (24).h(),
}}
Related
TL;DR:
I can't draw an image exactly onto the full screen on wide-aspect (13:6) phones. If I observe the safe area, the error is (predictably) underscan. Using .edgesIgnoringSafeArea() goes (unexpectedly) too far in the other direction.
Update
Apple DTS have suggested this is a bug, refunded me one support incident, and invited me to submit a bug report. It is in the pipeline at https://feedbackassistant.apple.com/feedback/8192204
Caveat Lector
My presumptions about .scaledToFill might be wrong. I address that at the end.
Code
So elementary I can put it here and it won't even slow you down
struct ContentView: View {
var body: some View {
Image("testImage").resizable().scaledToFill()
// .edgesIgnoringSafeArea(.all)
}
}
Test Image
The Test Image is a landscape rectangle, proportioned at 13:6, like the wide phone. (E.g. the 812:375 proportion of the original iPhone X.) The gray periphery is not part of the image.
It has its sub-frames marked, that correspond to the narrow (older) phones (16:9) and pads (4:3).
Runtime Results
The Xcode project settings are explicitly landscape-only, for both pads and phones.
For narrow phones and all pads, the code above, observing safe areas, renders the Test Image like I expect:
But on wide phones, I can't get the red rect to coincide with the screen edges.
Wide Phones
With no call to .edgesIgnoringSafeArea(), that is we are observing safe area. Naturally, our image is mapped to a subset of the full screen.
With the call to .edgesIgnoringSafeArea(). I expected this to exactly fill the screen but it overscans:
Here is the Xcode view-hierarchy debugger's perspective on the previous: the image is being mapped to a rect larger than the full screen. Why?
Order of Events
If I reverse the order of modifiers, and call .edgesIgnoringSafeArea() before .scaledToFill(), I get aspect ratio distortion, which .scaledToFill() is supposed to prevent. (See circle become ellipse in screen shot.) An explanation of how these operations compose, and why they do not commute, might go a long way to answering my primary questions.
Workaround
I think the above should work, and I don't see why not. What does work — on wide phones — is to eliminate the .scaledToFill modifier. Then you get this. But it only works because the test image is already the exact aspect ratio as the display — not a very general solution.
Scale to Fill
In the restricted domain of landscape images and displays, I expect the operation of scale-to-fill on the 13:6 test image to be equivalent to (to have the semantics of):
Center the test image in the destination (container) rect, sized to fit entirely in the container.
I have been expecting that ignoring safe areas means the "destination" will be the full screen, but that may be where I err.
Expand the test image, maintaining proportion and center, until one pair of sides coincides with those of the container.
For narrower displays, the left and right edges will meet first, and the top and bottom will be inside the destination rect.
But don’t stop now. That would be scale to fit, or letterboxing.
Expand until your top and bottom coincide with those of the container.
For narrower displays this means there will be content cropped on both sides
For 13:6 displays all four image edges will to coincide with the display edges at the same time.
I do not know why .edgesIgnoringSafeArea() does not work as it should but here is a workaround that should help you.
GeometryReader { geo in
Image("testImage")
.resizable()
.scaledToFill()
.frame(width: geo.size.width, height: geo.size.height)
}
.edgesIgnoringSafeArea(.all)
Update:
Here is another way to do the same thing without GeometryReader:
Image("testImage")
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
I'm using fabricjs 1.7.17 and have seen this issue below on IE Edge, Chrome and Firefox.
where the canvas is being set with a width and height in HTML of 768px.
Within my fabricjs canvas init I've got:
editor.canvas = new fabric.Canvas('editor')
editor.canvas.setDimensions({
width: 1080,
height: 1080,
allowTouchScrolling: false,
backgroundColor: 'rgba(55, 55, 55, .33)',
rotationCursor: 'url(/static/img/rotate.cur) 10 10, crosshair'
}, {backstoreOnly: true})
The problem is when you call the editor.canvas.toDataURL() method the size of the goes from 768px to 1080px
If you call the browser's native canvas.toDataURL() method there isn't a jump in size.
Anyone bump into this and have a possible fix?
Here's a simplified jsfiddle:
https://jsfiddle.net/m8dhmbtu/23/
Click on the "Native Canvas.toDataURL()" and you'll get the results to console.log and there will be no change to the displayed canvas in the preview window.
Click on the "Fabricjs canvas.toDataURL()" and you'll also get the results (the same) to console.log however; you'll also get a jump in the size of the in the preview window...this is what I'm trying to prevent.
I think the problem is actually a bug.
You set the canvas to 1080 for backstore dimension only, meaning that the canvas will be sized but the css will be left untouched.
A dataurl will reset the canvas after the process to fix it back in case the export to data url had a multiplier.
while this can be fixed at least for the non multiplied situations, a proper fix is needed.
The proper fix consist in setting backstore only during export so that the css does not get touched.
A better fix would be to do not touch the original canvas and create one on the fly just for exporting.
please go on official fabricjs repository and open an issue for this.
I am trying to setup a layout such that it has an image with regions. Those regions,can have either other ImageView or TextView. I am able to achieve this at a high level using FrameLayout and RelativeLayout. However, it does not scale properly on different devices.
As an example here is an image
Yellow/Red Region - ImageView setup dynamically
Blue/Green Region - TextView
So On the Device, I want it to look like this:
And like this:
Note that Image scales correctly and so does child views in correct regions.
Can someone please share some ideas on how this could be achieved?
I was able to do this by writing a Custom View. I set the layout bonds based on the % offset.
For example the image would start at 15% off top and 10% off left and width is 30% card size etc.
I tested this with multiple devices and so far it is working as designed.
Have a look at Supporting Different Screen Sizes
Also have a look at Supporting Multiple Screens
This links has exactly what you are looking for. Follow the example and you will achieve the desired result.
These tutorials explains how to scale images based on the screen sizes and how to design your layout.
Extract from Google's dev site:-
Explicitly declare in the manifest which screen sizes your application supports
Provide different layouts for different screen sizes
Provide different bitmap drawables for different screen densities
Use OnConfigurationChanged() to determine the orientation and changes the views according to your requirements
public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig)
{
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
//Change the Layout width and height attributes according to your requirements
Log.e("On Config Change","LANDSCAPE");
}else{
//Change the Layout width and height attributes according to your requirements
Log.e("On Config Change","PORTRAIT");
}
base.OnConfigurationChanged(newConfig);
}
I want to build a website that looks exactly the same across all screen width's, which means the whole website will scale according to the screen's, or more accurately, the viewport's width.
This is relatively easy to do for SVG images and I have all images correctly scaling according to the viewport's width. The viewport's width is the point of reference, from which all images scale. However, the point of reference for the text is different between any desktop browser and the iPhone's Safari (and I assume any mobile browser).
According to my research there seem to be two possible reasons for different sized text: a difference in the default CSS's or a difference in the rendering engines. Since I can't find any reference to pixel sized text on Chrome's default CSS or Firefox's default CSS, I assume this setting comes from the rendering engine.
My IP is dynamic so I can't provide a live example, but here are the screens comparing the same site in iPhone's Safari and Chrome on the desktop. Notice the huge difference in the size of the text.
Is there any way I can make the text have the same relative size in both these browsers?
I found the answer in JavaScript:
onresize=onload=function(){
document.body.style.fontSize=window.innerWidth/20+"px"
}
which sets the text size according to the viewport's width on the body element. Since all the text set in em's is sized in relation to their parents, all the text is sized correctly from the body element.
Furthermore, if you want to avoid the cascading hell by using rems and respect the original layout design from a let's say 1024px width you can stick with this:
onresize = onload = function(){
document.querySelector("html").style.fontSize = ( innerWidth * 100 ) / 1024 + "%";
}
You should try CSS Unit vw, like this:
body { font-size: 1.5vw; }
However, i am not sure it is supported by mobile browsers...
EDIT
Check for browser compatibility here.
I have an Android mobile app created with jQuery Mobile and PhoneGap. I would like to have 3 separate layouts depending on device and orientation:
Portrait phone - on smartphones I want to use always portrait layout
Portrait tablet - different than on phone
Landscape tablet
Is this possible to achieve with CSS media queries? I know changing layouts is possible, but not sure how to force portrait layout only for smartphones and at the same time allow landscape for tablets.
Most of the devices would come under the following 3 resolutions
1) HVGA-Half of VGA (320 x 240) 2) WVGA- wide VGA(800x 480) - nearly 1.5 times of HVGA 3) HVGA 2x- (640 X 960) - IPHONE 4 uses this resolution
for your requirements
1) & 2) you can write your styles inside #media as below. you can use HVGA or WVGA. Change width and height as per your need
#Media screen and (min-width: 320px) and (max-width: 480px){
/* css files here*/
}
3)For a landscape view use orientation:landscape like in below example
#media only screen and (-webkit-min-device-pixel-ratio: 2) and (orientation:landscape){
/* css files here */
}
The code piece is just for your reference . Your needs may be different, Please change parameters like screen/only ,min-device-pixel-ratio, max-device-pixel-ratio etc as per your needs