Reputation: 2335
is there any way to understand which type of Device the user is using.
I would like to use it in order to change the font size for the iPhone8 , if device == "iPhone8" { use Textfontsize 20 } else { use 10}
thanks a lot
Upvotes: 3
Views: 3170
Reputation: 758
The OP has already identified a successful answer but I'm writing my answer anyway in case someone else finds this useful.
The first answer has the downside that you need to update the code every time when new devices appear on the market.
The second answer is good because it reminds you to use Dynamic Type, but it does not really answer the OP who wants to scale the font based on the device (or device size) instead of user preference.
I assume that the OP has apparently the issue that different devices have different screen sizes, and the UI layout is static but it should scale to the screen size somehow. So what you want is that fonts and areas are proportional to the screen size such that your fixed layout always fits the screen. This can happen for example in 2D games without scrolling areas and where you just want proportional regions. Simply upscaling a SpriteKit scene won't work because devices have different aspect ratios so you'd get stretched texts, and black bars are awkward and just unused space.
Here's an approach I have been using for a game:
class Metrics: ObservableObject {
static let standard = Metrics() // with standard sizes (factor 1), independent of display size
static let shared = Metrics() // sizes multiplied with factor
@Published var fontSizes: [FontContext: CGFloat]
enum FontContext {
case viewTitle
case viewSubtitle
case viewContent
case medal
case titleButton
case settingsRow
case footNote
case pickerTitle
case pickerContent
}
subscript(index: FontContext) -> Font { .custom(gameFontName, fixedSize: fontSizes[index]!) }
let gameFontName = "Optima-Bold"
let boldFontName = "Optima-ExtraBlack"
func font(_ fontContext: FontContext) -> UIFont { UIFont(name: gameFontName, size: fontSizes[fontContext]!)! }
func font(_ size: CGFloat) -> UIFont { UIFont(name: gameFontName, size: size)! }
// Font sizes below are 1:1 for iPad Air 10.9"
private let standardHeight: CGFloat = 820 // iPad reference screen height (landscape)
static let standardFontSizes: [FontContext: CGFloat] = [
.viewTitle: 40,
.viewSubtitle: 30,
.viewContent: 28,
.medal: 50,
.titleButton: 32,
.settingsRow: 24,
.pickerTitle: 22,
.pickerContent: 18,
]
init() {
fontSizes = Metrics.standardFontSizes
}
func setScreenSize(_ size: CGSize) {
let factor = size.height/standardHeight
fontSizes = Metrics.standardFontSizes.mapValues({ ($0*factor).rounded(.down) })
}
}
Then, somewhere in your app, you need to call setScreenSize()
where you know the size:
struct ContentView: View {
var body: some View {
GeometryReader { geom in
MyView()
.onAppear {
Metrics.shared.setScreenSize(geom.size)
}
}
}
}
The idea is to create font presets that you use throughout the app which creates a sense of consistency.
You call Metrics.shared.font()
wherever you need a font, and it will be larger on larger devices and smaller on smaller devices. Call Metrics.standard.font()
to get your default font size where you don't want scaled fonts.
In the end I am not using these scaled fonts because I decided that if displays are larger then so be it, and the dpi won't change much anyway, it's just the resolution that changes with new devices. My game is mostly using SpriteKit so it has its own font scaling method anyway which calculates font sizes based on the geometric shapes they are contained in.
At least this class is useful for having font presets, that's already helpful (like text formats in word processors).
Upvotes: 1
Reputation: 8995
Damiano,
Changing the font size based on the device isn't a good plan, what happens when Apple launch a new device. What does your user who uses accessibility and wants to make the font larger do? Maybe you have a user who wants small fonts cause he is using a 12inch iPad in dual screen mode.
Use dynamic fonts, and let the user choose the size they like :) Here is cut'n'paste from an article on that very same subject.
https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-dynamic-type-with-a-custom-font
@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
struct ScaledFont: ViewModifier {
@Environment(\.sizeCategory) var sizeCategory
var name: String
var size: CGFloat
func body(content: Content) -> some View {
let scaledSize = UIFontMetrics.default.scaledValue(for: size)
return content.font(.custom(name, size: scaledSize))
}
}
@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
extension View {
func scaledFont(name: String, size: CGFloat) -> some View {
return self.modifier(ScaledFont(name: name, size: size))
}
}
struct ContentView: View {
var body: some View {
List {
Text("Hello World")
Text("Hello World")
.scaledFont(name: "Georgia", size: 12)
}
}
}
Upvotes: 4
Reputation: 145
If you want to handle different font size for different iPhone models. You have to create custom class for that.
import Foundation
import UIKit
// for label
class ClassLabelSizeClass: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
switch UIDevice().type {
case .iPhoneSE, .iPhone5, .iPhone5S, .iPhone5C:
self.font = self.font.withSize(?*self.font.pointSize)
case .iPhone6, .iPhone6S, .iPhone7, .iPhone8:
self.font = self.font.withSize(?*self.font.pointSize + 2)
case .iPhone6plus, .iPhone6Splus, .iPhone7plus, .iPhone8plus:
self.font = self.font.withSize(?*self.font.pointSize + 2)
case .iPhoneX, .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhone11, .iPhone11Pro, .iPhone11ProMax:
self.font = self.font.withSize(?*self.font.pointSize + 3)
default:
self.font = self.font.withSize(?*self.font.pointSize + 4)
}
}
}
//for textfield
class ClassTextFieldSizeClass: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
switch UIDevice().type {
case .iPhoneSE, .iPhone5, .iPhone5S, .iPhone5C:
self.font = self.font?.withSize(?*self.font?.pointSize)
case .iPhone6, .iPhone6S, .iPhone7, .iPhone8:
self.font = self.font?.withSize(?*self.font?.pointSize + 2)
case .iPhone6plus, .iPhone6Splus, .iPhone7plus, .iPhone8plus:
self.font = self.font?.withSize(?*self.font?.pointSize + 2)
case .iPhoneX, .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhone11, .iPhone11Pro, .iPhone11ProMax:
self.font = self.font?.withSize(?*self.font?.pointSize + 3)
default:
self.font = self.font?.withSize(?*self.font?.pointSize + 4)
}
}
}
//for button
class ClassButtonSizeClass: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
switch UIDevice().type {
case .iPhoneSE, .iPhone5, .iPhone5S, .iPhone5C:
self.titleLabel?.font = self.titleLabel?.font.withSize(?*self.titleLabel?.font.pointSize)
case .iPhone6, .iPhone6S, .iPhone7, .iPhone8:
self.titleLabel?.font = self.titleLabel?.font.withSize(?*self.titleLabel?.font.pointSize + 2)
case .iPhone6plus, .iPhone6Splus, .iPhone7plus, .iPhone8plus:
self.titleLabel?.font = self.titleLabel?.font.withSize(?*self.titleLabel?.font.pointSize + 2)
case .iPhoneX, .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhone11, .iPhone11Pro, .iPhone11ProMax:
self.titleLabel?.font = self.titleLabel?.font.withSize(?*self.titleLabel?.font.pointSize + 3)
default:
self.titleLabel?.font = self.titleLabel?.font.withSize(?*self.titleLabel?.font.pointSize + 4)
}
}
}
// for textView
class ClassTextViewSizeClass: UITextView {
override func awakeFromNib() {
super.awakeFromNib()
switch UIDevice().type {
case .iPhoneSE, .iPhone5, .iPhone5S, .iPhone5C:
self.font = self.font?.withSize(?*self.font?.pointSize)
case .iPhone6, .iPhone6S, .iPhone7, .iPhone8:
self.font = self.font?.withSize(?*self.font?.pointSize + 2)
case .iPhone6plus, .iPhone6Splus, .iPhone7plus, .iPhone8plus:
self.font = self.font?.withSize(?*self.font?.pointSize + 2)
case .iPhoneX, .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhone11, .iPhone11Pro, .iPhone11ProMax:
self.font = self.font?.withSize(?*self.font?.pointSize + 3)
default:
self.font = self.font?.withSize(?*self.font?.pointSize + 4)
}
}
}
//for custom value returning
class ClassUIDeviceTypeReturn {
static let shared = ClassUIDeviceTypeReturn()
func returnFloatValue(iPhone5: CGFloat, iPhone6: CGFloat, iPhone6Plus: CGFloat, iPhoneX: CGFloat, iPadDefault: CGFloat) -> CGFloat {
switch UIDevice().type {
case .iPhoneSE, .iPhone5, .iPhone5S, .iPhone5C:
return iPhone5
case .iPhone6, .iPhone6S, .iPhone7, .iPhone8:
return iPhone6
case .iPhone6plus, .iPhone6Splus, .iPhone7plus, .iPhone8plus:
return iPhone6Plus
case .iPhoneX, .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhone11, .iPhone11Pro, .iPhone11ProMax:
return iPhoneX
default:
return iPadDefault
}
}
}
public enum EnumModel: String {
case simulator = "simulator/sandbox",
//iPod
iPod1 = "iPod 1",
iPod2 = "iPod 2",
iPod3 = "iPod 3",
iPod4 = "iPod 4",
iPod5 = "iPod 5",
//iPad
iPad2 = "iPad 2",
iPad3 = "iPad 3",
iPad4 = "iPad 4",
iPadAir = "iPad Air ",
iPadAir2 = "iPad Air 2",
iPad5 = "iPad 5", //aka iPad 2017
iPad6 = "iPad 6", //aka iPad 2018
//iPad mini
iPadMini = "iPad Mini",
iPadMini2 = "iPad Mini 2",
iPadMini3 = "iPad Mini 3",
iPadMini4 = "iPad Mini 4",
//iPad pro
iPadPro9_7 = "iPad Pro 9.7\"",
iPadPro10_5 = "iPad Pro 10.5\"",
iPadPro12_9 = "iPad Pro 12.9\"",
iPadPro2_12_9 = "iPad Pro 2 12.9\"",
//iPhone
iPhone4 = "iPhone 4",
iPhone4S = "iPhone 4S",
iPhone5 = "iPhone 5",
iPhone5S = "iPhone 5S",
iPhone5C = "iPhone 5C",
iPhone6 = "iPhone 6",
iPhone6plus = "iPhone 6 Plus",
iPhone6S = "iPhone 6S",
iPhone6Splus = "iPhone 6S Plus",
iPhoneSE = "iPhone SE",
iPhone7 = "iPhone 7",
iPhone7plus = "iPhone 7 Plus",
iPhone8 = "iPhone 8",
iPhone8plus = "iPhone 8 Plus",
iPhoneX = "iPhone X",
iPhoneXS = "iPhone XS",
iPhoneXSMax = "iPhone XS Max",
iPhoneXR = "iPhone XR",
iPhone11 = "iPhone 11",
iPhone11Pro = "iPhone 11 Pro",
iPhone11ProMax = "iPhone 11 Pro Max",
//Apple TV
AppleTV = "Apple TV",
AppleTV_4K = "Apple TV 4K",
unrecognized = "?unrecognized?"
}
// #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
// MARK: UIDevice extensions
// #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
public extension UIDevice {
public var type: EnumModel {
var systemInfo = utsname()
uname(&systemInfo)
let modelCode = withUnsafePointer(to: &systemInfo.machine) {
$0.withMemoryRebound(to: CChar.self, capacity: 1) {
ptr in String.init(validatingUTF8: ptr)
}
}
var modelMap : [ String: EnumModel ] = [
"i386": .simulator,
"x86_64": .simulator,
//iPod
"iPod1,1": .iPod1,
"iPod2,1": .iPod2,
"iPod3,1": .iPod3,
"iPod4,1": .iPod4,
"iPod5,1": .iPod5,
//iPad
"iPad2,1": .iPad2,
"iPad2,2": .iPad2,
"iPad2,3": .iPad2,
"iPad2,4": .iPad2,
"iPad3,1": .iPad3,
"iPad3,2": .iPad3,
"iPad3,3": .iPad3,
"iPad3,4": .iPad4,
"iPad3,5": .iPad4,
"iPad3,6": .iPad4,
"iPad4,1": .iPadAir,
"iPad4,2": .iPadAir,
"iPad4,3": .iPadAir,
"iPad5,3": .iPadAir2,
"iPad5,4": .iPadAir2,
"iPad6,11": .iPad5, //aka iPad 2017
"iPad6,12": .iPad5,
"iPad7,5": .iPad6, //aka iPad 2018
"iPad7,6": .iPad6,
//iPad mini
"iPad2,5": .iPadMini,
"iPad2,6": .iPadMini,
"iPad2,7": .iPadMini,
"iPad4,4": .iPadMini2,
"iPad4,5": .iPadMini2,
"iPad4,6": .iPadMini2,
"iPad4,7": .iPadMini3,
"iPad4,8": .iPadMini3,
"iPad4,9": .iPadMini3,
"iPad5,1": .iPadMini4,
"iPad5,2": .iPadMini4,
//iPad pro
"iPad6,3": .iPadPro9_7,
"iPad6,4": .iPadPro9_7,
"iPad7,3": .iPadPro10_5,
"iPad7,4": .iPadPro10_5,
"iPad6,7": .iPadPro12_9,
"iPad6,8": .iPadPro12_9,
"iPad7,1": .iPadPro2_12_9,
"iPad7,2": .iPadPro2_12_9,
//iPhone
"iPhone3,1": .iPhone4,
"iPhone3,2": .iPhone4,
"iPhone3,3": .iPhone4,
"iPhone4,1": .iPhone4S,
"iPhone5,1": .iPhone5,
"iPhone5,2": .iPhone5,
"iPhone5,3": .iPhone5C,
"iPhone5,4": .iPhone5C,
"iPhone6,1": .iPhone5S,
"iPhone6,2": .iPhone5S,
"iPhone7,1": .iPhone6plus,
"iPhone7,2": .iPhone6,
"iPhone8,1": .iPhone6S,
"iPhone8,2": .iPhone6Splus,
"iPhone8,4": .iPhoneSE,
"iPhone9,1": .iPhone7,
"iPhone9,3": .iPhone7,
"iPhone9,2": .iPhone7plus,
"iPhone9,4": .iPhone7plus,
"iPhone10,1": .iPhone8,
"iPhone10,4": .iPhone8,
"iPhone10,2": .iPhone8plus,
"iPhone10,5": .iPhone8plus,
"iPhone10,3": .iPhoneX,
"iPhone10,6": .iPhoneX,
"iPhone11,2": .iPhoneXS,
"iPhone11,4": .iPhoneXSMax,
"iPhone11,6": .iPhoneXSMax,
"iPhone11,8": .iPhoneXR,
"iPhone12,1": .iPhone11,
"iPhone12,3": .iPhone11Pro,
"iPhone12,5": .iPhone11ProMax,
//AppleTV
"AppleTV5,3": .AppleTV,
"AppleTV6,2": .AppleTV_4K
]
if let model = modelMap[String.init(validatingUTF8: modelCode!)!] {
if model == .simulator {
if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
if let simModel = modelMap[String.init(validatingUTF8: simModelCode)!] {
return simModel
}
}
}
return model
}
return EnumModel.unrecognized
}
}
Don't forget to give custom class to label, textfield, button, textView from storyboard.
Hope it helps :)
Upvotes: 5