In some apps theres settings for screen sizes. Vs regular constraint settings, whats the advantage vs disadvantage?
example:
struct Setting {
struct Small {
static let phoneSE: CGFloat = 11.0
static let phone: CGFloat = 13.0
static let phonePlus: CGFloat = 14.0
}
struct Medium {
static let phoneSE: CGFloat = 12.0
static let phone: CGFloat = 14.0
static let phonePlus: CGFloat = 15.0
}
struct Large {
static let phoneSE: CGFloat = 13.0
static let phone: CGFloat = 15.0
static let phonePlus: CGFloat = 16.0
}
These values are specific font size values.
The values often change for the purpose, some values might be font size or img sizes. Is something like this a good practice or overkill?
It usually isn't "good practice" to code against specific devices. Or in other words, it is "good practice to code against size classes (using auto layout constraints) and use Dynamic Type (instead of specific font sizes) where possible. Why? Because when Apple releases something new - like the 10.5 inch iPad Pro last April or the rumored iPhone Pro this September - you won't have to do much, if anything to your code.
It will "just work".
That said, here's some reasons you may need to code against specific devices - or more accurately, specific screen sizes:
UI changes for orientation on an iPad and iPhone "Plus". Size classes are always regular no matter the orientation.
Font sizes, like in your example.
I'm sure there's more. But in these two cases - and really, in all cases - it's "good practice" to try hard to use what Apple gives you. Refactor your UI if possible. Save on unnecessary ongoing maintenance.
It depends on your requirements.
If you need to display different size depend on device you can create structure for that if not than you can use fix size.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 months ago.
Improve this question
I'm making a zoom control for my app, and I'd like to make it advanced, like in default camera app from Apple: sample
I did some research, and still have some questions about it.
Is it possible to get focal length value programmaticaly? There are labels like 13mm, 26mm for different back cameras in the default app, but there is no such property on AVCaptureDevice. (It is probably needed to determine zoom values, see the next question)
How can we determine zoom values to display in UI? The thing is that AVCaptureDevice's minZoomFactor always starts from 1x, but in the camera app we can see that on devices with ultrawide camera the scale starts at 0.5x, so there should be some way to map this values onto each other. As I understand, Apple considers "usual" back camera as default (that is, 1x), and all other values are relative to it: 13mm is 0.5 * 26mm, so the first value on iphone 13 pro zoom control will be 0.5x, the second value is the "default" and is 1x (26mm), and telephoto camera is 77mm, so the third value is 3x (26mm * 3 = 78mm ~= 77mm). Please clarify how it is actually calculated and correct me if my assumption is wrong.
What is the correct way to get max zoom value? If I try AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInTripleCamera], mediaType: .video, position: .back).devices.first!.maxAvailableVideoZoomFactor, it says 123.75 (iphone 13 pro), but in the default camera app max zoom value is 15x. Why is it exactly 15x and where does it come from? (My assumption is that max digital zoom for all iPhones equals to 5x, so on 13 Pro telephoto camera zooms 3x as "usual" camera, thus we get 3x * 5x = 15x max zoom)
Is there any universal way to get "the best" (i.e with all features) camera? For example, now I can specify [.builtInTripleCamera, .builtInDualWideCamera, .builtInDualCamera, .builtInWideAngleCamera] for discovery session and pick the first item in devices array, but if Apple will release, lets say, some ".builtInQuadrupleCamera" in a couple of years, this code will have to be modified, because it won't include it automatically.
To sum up (TL;DR version):
As I suppose, final code should look something like this:
let deviceTypes: [AVCaptureDevice.DeviceType]
if #available(iOS 13, *) {
deviceTypes = [.builtInTripleCamera, .builtInDualWideCamera, .builtInDualCamera, .builtInWideAngleCamera]
} else {
deviceTypes = [.builtInDualCamera, .builtInWideAngleCamera]
}
let session: AVCaptureDevice.DiscoverySession(
deviceTypes: deviceTypes,
mediaType: .video,
position: .back
)
if let device = session.devices.first {
device.getUIZoomValues()
}
extension AVCaptureDevice {
func getUIZoomValues() -> [Float] {
// Hardcode. Seems like all iPhones limit digital zoom to 5x
let maxDigitalZoom: Float = 5
// fallback for old iOS versions
guard #available(iOS 13, *) else { return [1, maxDigitalZoom] }
let uiZoomValues: [Float]
let factors = virtualDeviceSwitchOverVideoZoomFactors
switch deviceType {
case .builtInTripleCamera, .builtInDualWideCamera:
// ultrawide camera is available - starting zoom from 0.5x
let firstZoom: Float = 1.0 / factors.first!.floatValue
uiZoomValues = [firstZoom] + factors.map { $0.floatValue * firstZoom } + [firstZoom * factors.last!.floatValue * maxDigitalZoom]
case .builtInDualCamera:
// no ultrawide. Starting from 1x
uiZoomValues = [1.0] + factors.map { $0.floatValue } + [factors.last!.floatValue * maxDigitalZoom]
case .builtInWideAngleCamera:
// just a single "usual" camera.
uiZoomValues = [1, maxDigitalZoom]
default:
fatalError("this should not happen on a real device")
}
return uiZoomValues
}
}
2 main concerns about this code:
1 - We have to hardcode maxDigitalZoom. Is there any way to get it programmaticaly? Apple states 5x in iPhone specs, and there is AVCaptureDevice.maxAvailableVideoZoomFactor, but those values are different (for example, iPhone 13 pro has 15x in specs vs 123.75x in maxAvailableVideoZoomFactor).
2 - Case builtInDualCamera (iPhone XS Max, for example). All the code above relies on virtualDeviceSwitchOverVideoZoomFactors var, which is available only from iOS 13, but builtInDualCamera is available from iOS 10.2, so what will happen if user has XS Max? Will it work on iOS >= 13 but break on earlier versions? Or it will not work at all?
Questions in order:
I think not
Works for me:
Created dictionary var zoomFactors: [String: CGFloat] = ["1": 1]
Than manage AVCaptureDevice?
I think u can play with getApproximation() to achieve the goal
No
Code Questions:
No. but i think one of the easiest method it's to play with getApproximation() idea
I believe it will be crash
I'm making a universal game across all apple platforms. The problem is there are lots of aspect ratios, and with the growing numbers of devices, it becomes a hassle. I've tried the following:
var deviceAspectRatio: CGFloat? {
#if os(iOS)
if UIDevice.current.model.contains("iPhone") {
return 16/9
} else if UIDevice.current.model.contains("iPad") {
return 4/3
}
#elseif os(tvOS)
return 16/9 //There might be other aspect ratios also
#elseif os(watchOS)
return 1
#elseif os(macOS)
//figure out aspect ratio
#else
return nil
#endif
}
But even with this, Xcode gives me an error:
Missing return in a function expected to return 'CGFloat?'
The trick on macOS is that there might be more than one screen, so if this is the case, you'll have to decide which one you're interested in. However, if you settle on a screen, you can just get the frame from NSScreen.frame and divide the width by the height.
This code will get the aspect ratio for the screen a given window is on:
guard let frame = someWindow.screen?.frame else { return nil }
let aspectRatio = NSWidth(frame) / NSHeight(frame)
Also, you should probably be doing something similar with UIScreen on iOS instead of hard-coding the values there. Apple may someday release a new device with some other aspect ratio your app doesn't anticipate.
In order to make it universal code for all devices you can use DeviceKit dependency and then
import DeviceKit
let device = Device.current
let deviceAspectRatio = device.screenRatio.height / device.screenRatio.width
OR
let deviceWidth = UIScreen.main.bounds.width * UIScreen.main.scale
let deviceHeight = UIScreen.main.bounds.height * UIScreen.main.scale
let testDeviceAspectRatio = deviceHeight / deviceWidth
I'm maintaining an enum with the majority of font sizes used in my app like so -
enum FontSize : CGFloat
{
case HeaderSize = 20
case TitleSize = 18
case PrimaryButtonTextSize = 22
}
I have written this convenience method in a UIFont extension that I wish to use across the app like -
static func fontWithSize(size:FontSize) -> UIFont?
{
return font = UIFont(name:"System" , size: size.rawValue)
}
USAGE -
headerLabel.font = UIFont.fontWithSize(.HeaderSize)
Now I'm facing 2 issues with this design -
#1
Since I'm identifying font sizes by where they are used in UI I would most likely add more cases in the future as -
enum FontSize : CGFloat
{
case HeaderSize = 20
case TitleSize = 18
case PrimaryButtonTextSize = 22
// Newly added
case SpecialButtonTextSize = 20
case SpecialTitleTextSize = 10
}
The problem is that swift enum will not allow me to add multiple cases with same value. Of course I could just remove SecondaryButtonTextSize or SpecialTitleTextSize and always use .HeaderSize -
specialButton.font = UIFont.fontWithSize(.HeaderSize)
specialTitle.font = UIFont.fontWithSize(.HeaderSize)
But this would mean that if I decide to change HeaderSize later I'd be missing out on my special button and special title cases. Also call me crazy but I'd be happier if I could use those enums like that.
#2
Now of course this enum will not be an exhaustive list of all font sizes my app uses. There'll be those one-off cases where I'll have to pass in a size value for a one time use only.
One solution is to just have another method like -
static func fontWithSize(size:CGFloat) -> UIFont?
{
return font = UIFont(name:"System" , size: size)
}
But It'd be awesome if somehow I could continue using my enum and make it return a custom value. maybe it'd look something like
enum FontSize : CGFloat
{
case HeaderSize = 20
case TitleSize = 18
case PrimaryButtonTextSize = 22
case CustomSize(CGFloat) -> CGFloat // lolwut?
}
I know this might be a trivial thing to worry about, but it'd be great to have some design solution for this issue. Maybe enums aren't the solution at all! Any pointers will help!
Your enum have init(rawValue: CGFloat) initializer. If you'll use the same rawValue for different cases, it won't be able to decide which case produce from this value. So it's no surprise compiler doesn't allow to do that.
Solution to your problem is simple: get rid of cases and use static vars instead:
enum FontSize {
static var HeaderSize: CGFloat = 20.0
static var SpecialButtonTextSize: CGFloat = 20.0
static var SpecialTitleTextSize: CGFloat = 10.0
}
I've created an app with the latest version of AIR (16.0.0.272) and I'm trying to scale the content to fit any resolution of the iPhone from 4 to 6+
In desktop debugging I see all perfectly scaled changing the stage dimension to test my solution. Trying on my iPhone 5 I can't see the scaled content.
If I comment the resize function everything is ok (I see all the content, not scaled obviously).
That's my solution
private function resize(e:Event=null):void{
w=stageW();
h=stageH();
var initW:Number=640;
var initH:Number=960;
//bg.img.width=w;
trace(w+"/"+h);
main.y=62;
//main.x=w*.5-320;
bg.width=w;
bg.height=h;
menu.bg.width=w;
menu.bg.height=h;
var divisor:Number = 640/w;
main.width = Math.floor(main.width / divisor);
main.height = Math.floor(main.height / divisor);
}
I have tried to temporize the resize call to test it on the iPhone but again after 2000ms I can't see anything.
After this I've tried with a listener addEventListener(Event.ADDED_TO_STAGE, init); calling the resize above at the end of my operations of building UI and so on.
Can't figure why resizing a movieclip that contains my app content make it disappear from iPhone and not from the desktop.
Hope in a solution
Thanks in advance
Capabilities.screenResolutionX and Capabilities.screenResolutionY are what you should use for screen width and height on apps.
Note: the X and Y don't change with rotation - they'll stay the same when screen rotates, so your code to get screen dimensions will look like this:
if (bStagePortrait) {
iScreenWidth = Capabilities.screenResolutionX;
iScreenHeight = Capabilities.screenResolutionY;
} else {
iScreenWidth = Capabilities.screenResolutionY;
iScreenHeight = Capabilities.screenResolutionX;
}
Also use this code to prevent app from resizing or moving:
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
For a variety of reasons, your resize function could be getting called more than once.
main.width = Math.floor(main.width / divisor);
main.height = Math.floor(main.height / divisor);
will continuously make the main clip smaller. It may be better to use a constant or number for size calculation. I usually do something with scaling, though.
main.scaleX = main.scaleY = percentStageScaled;
In the WWDC 2014 Platform State of the Union lecture Apple Engineer introduced size classes and he said we can think iPhone size UI as a compact size class and iPad size class as a regular size class. But size classes are not bounded to a particular device. They are a lot more general then that. If a viewcontroller look like an iPhone - its aspect ration is similar to it -, it will have a compact size class.
Is it a way to see at a certain time which size class was used by viewcontroller? I found contradictions between simulator content and Interface Builder preview and I would like to dig deeper into and see why it happens.
I am currently looking on how to use size classes programatically here are some finding that can be useful for you:
The easiest way to find out is to just check on the view controllers traitCollection
po self.traitCollection
Or listen to transitions:
First use the UIContentContainer protocol
#interface ViewController : UIViewController<UIContentContainer>
Then implement willTransitionToTraitCollection:
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
}
The newCollection element has information that can be used for your purpose, here is the info returned when rotating an iPhone 6 to landscape:
<UITraitCollection: 0x7f9ad152f320; _UITraitNameUserInterfaceIdiom = Phone, _UITraitNameDisplayScale = 2.000000, _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Compact, _UITraitNameTouchLevel = 0, _UITraitNameInteractionModel = 1>
And to portrait:
<UITraitCollection: 0x7f9ad142d770; _UITraitNameUserInterfaceIdiom = Phone, _UITraitNameDisplayScale = 2.000000, _UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Regular, _UITraitNameTouchLevel = 0, _UITraitNameInteractionModel = 1>
From there you can see it uses Compact Size Class for both horizontal and vertical in landscape, but uses Regular Size Class for Vertical when in portrait.