Screen.swift 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //
  2. // Screen.swift
  3. // ┌─┐ ┌───────┐ ┌───────┐
  4. // │ │ │ ┌─────┘ │ ┌─────┘
  5. // │ │ │ └─────┐ │ └─────┐
  6. // │ │ │ ┌─────┘ │ ┌─────┘
  7. // │ └─────┐│ └─────┐ │ └─────┐
  8. // └───────┘└───────┘ └───────┘
  9. //
  10. // Created by lee on 2018/1/22.
  11. // Copyright © 2018年 lee. All rights reserved.
  12. //
  13. import Foundation
  14. #if os(iOS)
  15. import UIKit
  16. public class ScreenWrapper<Base> {
  17. let base: Base
  18. public private(set) var value: Base
  19. init(_ base: Base) {
  20. self.base = base
  21. self.value = base
  22. }
  23. }
  24. public protocol ScreenCompatible {
  25. associatedtype ScreenCompatibleType
  26. var screen: ScreenCompatibleType { get }
  27. }
  28. extension ScreenCompatible {
  29. public var screen: ScreenWrapper<Self> {
  30. get { return ScreenWrapper(self) }
  31. }
  32. }
  33. extension ScreenWrapper {
  34. public func width(_ types: Screen.Width..., is value: Base) -> Self {
  35. return width(types, is: value, zoomed: value)
  36. }
  37. public func width(_ types: Screen.Width..., is value: Base, zoomed: Base) -> Self {
  38. return width(types, is: value, zoomed: zoomed)
  39. }
  40. private func width(_ types: [Screen.Width], is value: Base, zoomed: Base) -> Self {
  41. for type in types where Screen.Width.current == type {
  42. self.value = Screen.isZoomedMode ? zoomed : value
  43. }
  44. return self
  45. }
  46. public func height(_ types: Screen.Height..., is value: Base) -> Self {
  47. return height(types, is: value, zoomed: value)
  48. }
  49. public func height(_ types: Screen.Height..., is value: Base, zoomed: Base) -> Self {
  50. return height(types, is: value, zoomed: zoomed)
  51. }
  52. private func height(_ types: [Screen.Height], is value: Base, zoomed: Base) -> Self {
  53. for type in types where Screen.Height.current == type {
  54. self.value = Screen.isZoomedMode ? zoomed : value
  55. }
  56. return self
  57. }
  58. public func inch(_ types: Screen.Inch..., is value: Base) -> Self {
  59. return inch(types, is: value, zoomed: value)
  60. }
  61. public func inch(_ types: Screen.Inch..., is value: Base, zoomed: Base) -> Self {
  62. return inch(types, is: value, zoomed: zoomed)
  63. }
  64. private func inch(_ types: [Screen.Inch], is value: Base, zoomed: Base) -> Self {
  65. for type in types where Screen.Inch.current == type {
  66. self.value = Screen.isZoomedMode ? zoomed : value
  67. }
  68. return self
  69. }
  70. public func level(_ types: Screen.Level..., is value: Base) -> Self {
  71. return level(types, is: value, zoomed: value)
  72. }
  73. public func level(_ types: Screen.Level..., is value: Base, zoomed: Base) -> Self {
  74. return level(types, is: value, zoomed: zoomed)
  75. }
  76. private func level(_ types: [Screen.Level], is value: Base, zoomed: Base) -> Self {
  77. for type in types where Screen.Level.current == type {
  78. self.value = Screen.isZoomedMode ? zoomed : value
  79. }
  80. return self
  81. }
  82. }
  83. public enum Screen {
  84. public static var isZoomedMode: Bool {
  85. guard !isPlus else { return UIScreen.main.bounds.width == 375 }
  86. return UIScreen.main.scale != UIScreen.main.nativeScale
  87. }
  88. static var size: CGSize {
  89. UIScreen.main.bounds.size
  90. }
  91. static var nativeSize: CGSize {
  92. UIScreen.main.nativeBounds.size
  93. }
  94. static var scale: CGFloat {
  95. UIScreen.main.scale
  96. }
  97. static var nativeScale: CGFloat {
  98. UIScreen.main.nativeScale
  99. }
  100. public enum Width: CGFloat {
  101. case unknown = -1
  102. case _320 = 320
  103. case _375 = 375
  104. case _390 = 390
  105. case _414 = 414
  106. case _428 = 428
  107. public static var current: Width {
  108. guard !isPlus else { return ._414 }
  109. return Width(rawValue: nativeSize.width / scale) ?? .unknown
  110. }
  111. }
  112. public enum Height: CGFloat {
  113. case unknown = -1
  114. case _480 = 480
  115. case _568 = 568
  116. case _667 = 667
  117. case _736 = 736
  118. case _812 = 812
  119. case _844 = 844
  120. case _896 = 896
  121. case _926 = 926
  122. public static var current: Height {
  123. guard !isPlus else { return ._736 }
  124. return Height(rawValue: nativeSize.height / scale) ?? .unknown
  125. }
  126. }
  127. public enum Inch: Double {
  128. case unknown = -1
  129. case _3_5 = 3.5
  130. case _4_0 = 4.0
  131. case _4_7 = 4.7
  132. case _5_4 = 5.4
  133. case _5_5 = 5.5
  134. case _5_8 = 5.8
  135. case _6_1 = 6.1
  136. case _6_5 = 6.5
  137. case _6_7 = 6.7
  138. public static var current: Inch {
  139. guard !isPlus else {
  140. // Plus 机型比较特殊 下面公式无法正确计算出尺寸
  141. return ._5_5
  142. }
  143. switch (nativeSize.width / scale, nativeSize.height / scale, scale) {
  144. case (320, 480, 2):
  145. return ._3_5
  146. case (320, 568, 2):
  147. return ._4_0
  148. case (375, 667, 2):
  149. return ._4_7
  150. case (375, 812, 3) where UIDevice.iPhoneMini:
  151. return ._5_4
  152. case (414, 736, 3):
  153. return ._5_5
  154. case (375, 812, 3):
  155. return ._5_8
  156. case (414, 896, 2), (390, 844, 3):
  157. return ._6_1
  158. case (414, 896, 3):
  159. return ._6_5
  160. case (428, 926, 3):
  161. return ._6_7
  162. default:
  163. return .unknown
  164. }
  165. }
  166. }
  167. public enum Level: Int {
  168. case unknown = -1
  169. /// 3: 2
  170. case compact
  171. /// 16: 9
  172. case regular
  173. /// 19.5: 9
  174. case full
  175. public static var current: Level {
  176. guard !isPlus else {
  177. // Plus 机型比较特殊 下面公式无法正确计算出尺寸
  178. return .regular
  179. }
  180. switch (nativeSize.width / scale, nativeSize.height / scale) {
  181. case (320, 480):
  182. return .compact
  183. case (320, 568), (375, 667), (414, 736):
  184. return .regular
  185. case (375, 812), (414, 896), (390, 844), (428, 926):
  186. return .full
  187. default:
  188. return .unknown
  189. }
  190. }
  191. }
  192. private static var isPlus: Bool {
  193. return nativeSize.equalTo(.init(width: 1080, height: 1920))
  194. }
  195. }
  196. extension Screen {
  197. public static var isCompact: Bool {
  198. return Level.current == .compact
  199. }
  200. public static var isRegular: Bool {
  201. return Level.current == .regular
  202. }
  203. public static var isFull: Bool {
  204. return Level.current == .full
  205. }
  206. }
  207. extension Int: ScreenCompatible {}
  208. extension Bool: ScreenCompatible {}
  209. extension Float: ScreenCompatible {}
  210. extension Double: ScreenCompatible {}
  211. extension String: ScreenCompatible {}
  212. extension CGRect: ScreenCompatible {}
  213. extension CGSize: ScreenCompatible {}
  214. extension CGFloat: ScreenCompatible {}
  215. extension CGPoint: ScreenCompatible {}
  216. extension UIImage: ScreenCompatible {}
  217. extension UIColor: ScreenCompatible {}
  218. extension UIFont: ScreenCompatible {}
  219. extension UIEdgeInsets: ScreenCompatible {}
  220. fileprivate extension UIDevice {
  221. static var iPhoneMini: Bool {
  222. let temp = ["iPhone13,1", "iPhone14,4"]
  223. switch identifier {
  224. case "iPhone13,1", "iPhone14,4":
  225. return true
  226. case "i386", "x86_64", "arm64":
  227. return temp.contains(ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "")
  228. default:
  229. return false
  230. }
  231. }
  232. private static let identifier: String = {
  233. var systemInfo = utsname()
  234. uname(&systemInfo)
  235. let mirror = Mirror(reflecting: systemInfo.machine)
  236. let identifier = mirror.children.reduce("") { identifier, element in
  237. guard let value = element.value as? Int8, value != 0 else { return identifier }
  238. return identifier + String(UnicodeScalar(UInt8(value)))
  239. }
  240. return identifier
  241. } ()
  242. }
  243. #endif