QMUIHelper.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /**
  2. * Tencent is pleased to support the open source community by making QMUI_iOS available.
  3. * Copyright (C) 2016-2021 THL A29 Limited, a Tencent company. All rights reserved.
  4. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
  5. * http://opensource.org/licenses/MIT
  6. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
  7. */
  8. //
  9. // QMUIHelper.h
  10. // qmui
  11. //
  12. // Created by QMUI Team on 14/10/25.
  13. //
  14. #import <Foundation/Foundation.h>
  15. #import <UIKit/UIKit.h>
  16. #import "QMUICommonDefines.h"
  17. NS_ASSUME_NONNULL_BEGIN
  18. // TODO: molice 等废弃 qmui_badgeCenterOffset 系列接口后再删除
  19. extern const CGPoint QMUIBadgeInvalidateOffset;
  20. @interface QMUIHelper : NSObject
  21. + (instancetype)sharedInstance;
  22. /**
  23. 用一个 identifier 标记某一段 block,使其对应该 identifier 只会被运行一次
  24. @param block 要执行的一段逻辑
  25. @param identifier 唯一的标记,建议在 identifier 里添加当前这段业务的特有名称,例如用于 swizzle 的可以加“swizzled”前缀,以避免与其他业务共用同一个 identifier 引发 bug
  26. */
  27. + (BOOL)executeBlock:(void (NS_NOESCAPE ^)(void))block oncePerIdentifier:(NSString *)identifier;
  28. /**
  29. 将 UIViewContentMode 转为对应的 CALayerContentsGravity
  30. */
  31. + (CALayerContentsGravity)layerContentsGravityWithContentMode:(UIViewContentMode)contentMode;
  32. @end
  33. @interface QMUIHelper (Bundle)
  34. /// 获取 QMUIKit.framework Images.xcassets 内的图片资源
  35. /// @param name 图片名
  36. + (nullable UIImage *)imageWithName:(NSString *)name;
  37. @end
  38. @interface QMUIHelper (SystemVersion)
  39. + (NSInteger)numbericOSVersion;
  40. + (NSComparisonResult)compareSystemVersion:(nonnull NSString *)currentVersion toVersion:(nonnull NSString *)targetVersion;
  41. + (BOOL)isCurrentSystemAtLeastVersion:(nonnull NSString *)targetVersion;
  42. + (BOOL)isCurrentSystemLowerThanVersion:(nonnull NSString *)targetVersion;
  43. @end
  44. @interface QMUIHelper (DynamicType)
  45. /// 返回当前 contentSize 的 level,这个值可以在设置里面的“字体大小”查看,辅助功能里面有个“更大字体”可以设置更大的字体,不过这里我们这个接口将更大字体都做了统一,都返回“字体大小”里面最大值。
  46. /// Returns the level of contentSize
  47. /// The value can be set in Settings - Display & Brightness - Text Size as well as in General - Accessibility - Larger Text
  48. /// This method returns the value set by user or the maximum value in Text Size, whichever is smaller
  49. + (nonnull NSNumber *)preferredContentSizeLevel;
  50. /// 设置当前 cell 的高度,heights 是有七个数值的数组,对于不支持的iOS版本,则选择中间的值返回。
  51. /// Sets height of the cell; Heights consist of 7 numberic values; Returns the middle value on legacy iOS versions.
  52. + (CGFloat)heightForDynamicTypeCell:(nonnull NSArray *)heights;
  53. @end
  54. @interface QMUIHelper (Keyboard)
  55. /**
  56. * 判断当前 App 里的键盘是否升起,默认为 NO
  57. * Returns the visibility of the keybord. Default value is NO.
  58. */
  59. + (BOOL)isKeyboardVisible;
  60. /**
  61. * 记录上一次键盘显示时的高度(基于整个 App 所在的 window 的坐标系),注意使用前用 `isKeyboardVisible` 判断键盘是否显示,因为即便是键盘被隐藏的情况下,调用 `lastKeyboardHeightInApplicationWindowWhenVisible` 也会得到高度值。
  62. */
  63. + (CGFloat)lastKeyboardHeightInApplicationWindowWhenVisible;
  64. /**
  65. * 获取当前键盘frame相关
  66. * @warning 注意iOS8以下的系统在横屏时得到的rect,宽度和高度相反了,所以不建议直接通过这个方法获取高度,而是使用<code>keyboardHeightWithNotification:inView:</code>,因为在后者的实现里会将键盘的rect转换坐标系,转换过程就会处理横竖屏旋转问题。
  67. */
  68. + (CGRect)keyboardRectWithNotification:(nullable NSNotification *)notification;
  69. /// 获取当前键盘的高度,注意高度可能为0(例如第三方键盘会发出两次notification,其中第一次的高度就为0)
  70. + (CGFloat)keyboardHeightWithNotification:(nullable NSNotification *)notification;
  71. /**
  72. * 获取当前键盘在屏幕上的可见高度,注意外接键盘(iPad那种)时,[QMUIHelper keyboardRectWithNotification]得到的键盘rect里有一部分是超出屏幕,不可见的,如果直接拿rect的高度来计算就会与意图相悖。
  73. * @param notification 接收到的键盘事件的UINotification对象
  74. * @param view 要得到的键盘高度是相对于哪个View的键盘高度,若为nil,则等同于调用[QMUIHelper keyboardHeightWithNotification:]
  75. * @warning 如果view.window为空(当前View尚不可见),则会使用App默认的UIWindow来做坐标转换,可能会导致一些计算错误
  76. * @return 键盘在view里的可视高度
  77. */
  78. + (CGFloat)keyboardHeightWithNotification:(nullable NSNotification *)notification inView:(nullable UIView *)view;
  79. /// 获取键盘显示/隐藏的动画时长,注意返回值可能为0
  80. + (NSTimeInterval)keyboardAnimationDurationWithNotification:(nullable NSNotification *)notification;
  81. /// 获取键盘显示/隐藏的动画时间函数
  82. + (UIViewAnimationCurve)keyboardAnimationCurveWithNotification:(nullable NSNotification *)notification;
  83. /// 获取键盘显示/隐藏的动画时间函数
  84. + (UIViewAnimationOptions)keyboardAnimationOptionsWithNotification:(nullable NSNotification *)notification;
  85. @end
  86. @interface QMUIHelper (AudioSession)
  87. /**
  88. * 听筒和扬声器的切换
  89. *
  90. * @param speaker 是否转为扬声器,NO则听筒
  91. * @param temporary 决定使用kAudioSessionProperty_OverrideAudioRoute还是kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,两者的区别请查看本组的博客文章:http://km.oa.com/group/gyui/articles/show/235957
  92. */
  93. + (void)redirectAudioRouteWithSpeaker:(BOOL)speaker temporary:(BOOL)temporary;
  94. /**
  95. * 设置category
  96. *
  97. * @param category 使用iOS7的category,iOS6的会自动适配
  98. */
  99. + (void)setAudioSessionCategory:(nullable NSString *)category;
  100. @end
  101. @interface QMUIHelper (UIGraphic)
  102. /// 获取一像素的大小
  103. @property(class, nonatomic, readonly) CGFloat pixelOne;
  104. /// 判断size是否超出范围
  105. + (void)inspectContextSize:(CGSize)size;
  106. /// context是否合法
  107. + (BOOL)inspectContextIfInvalidated:(CGContextRef)context;
  108. @end
  109. @interface QMUIHelper (Device)
  110. /// 如 iPhone12,5、iPad6,8
  111. /// @NEW_DEVICE_CHECKER
  112. @property(class, nonatomic, readonly) NSString *deviceModel;
  113. /// 如 iPhone 11 Pro Max、iPad Pro (12.9 inch),如果是模拟器,会在后面带上“ Simulator”字样。
  114. /// @NEW_DEVICE_CHECKER
  115. @property(class, nonatomic, readonly) NSString *deviceName;
  116. @property(class, nonatomic, readonly) BOOL isIPad;
  117. @property(class, nonatomic, readonly) BOOL isIPod;
  118. @property(class, nonatomic, readonly) BOOL isIPhone;
  119. @property(class, nonatomic, readonly) BOOL isSimulator;
  120. @property(class, nonatomic, readonly) BOOL isMac;
  121. /// 带物理凹槽的刘海屏或者使用 Home Indicator 类型的设备
  122. /// @NEW_DEVICE_CHECKER
  123. @property(class, nonatomic, readonly) BOOL isNotchedScreen;
  124. /// 将屏幕分为普通和紧凑两种,这个方法用于判断普通屏幕(也即大屏幕)。
  125. /// @note 注意,这里普通/紧凑的标准是 QMUI 自行制定的,与系统 UITraitCollection.horizontalSizeClass/verticalSizeClass 的值无关。只要是通常意义上的“大屏幕手机”(例如 Plus 系列)都会被视为 Regular Screen。
  126. /// @NEW_DEVICE_CHECKER
  127. @property(class, nonatomic, readonly) BOOL isRegularScreen;
  128. /// iPhone 14 Pro Max
  129. @property(class, nonatomic, readonly) BOOL is67InchScreenAndiPhone14Later;
  130. /// iPhone 14 Plus / 13 Pro Max / 12 Pro Max
  131. @property(class, nonatomic, readonly) BOOL is67InchScreen;
  132. /// iPhone XS Max / 11 Pro Max
  133. @property(class, nonatomic, readonly) BOOL is65InchScreen;
  134. /// iPhone 12 / 12 Pro
  135. @property(class, nonatomic, readonly) BOOL is61InchScreenAndiPhone12Later;
  136. /// iPhone XR / 11
  137. @property(class, nonatomic, readonly) BOOL is61InchScreen;
  138. /// iPhone X / XS / 11Pro
  139. @property(class, nonatomic, readonly) BOOL is58InchScreen;
  140. /// iPhone 8 Plus
  141. @property(class, nonatomic, readonly) BOOL is55InchScreen;
  142. /// iPhone 12 mini
  143. @property(class, nonatomic, readonly) BOOL is54InchScreen;
  144. /// iPhone 8
  145. @property(class, nonatomic, readonly) BOOL is47InchScreen;
  146. /// iPhone 5
  147. @property(class, nonatomic, readonly) BOOL is40InchScreen;
  148. /// iPhone 4
  149. @property(class, nonatomic, readonly) BOOL is35InchScreen;
  150. @property(class, nonatomic, readonly) CGSize screenSizeFor67InchAndiPhone14Later;
  151. @property(class, nonatomic, readonly) CGSize screenSizeFor67Inch;
  152. @property(class, nonatomic, readonly) CGSize screenSizeFor65Inch;
  153. @property(class, nonatomic, readonly) CGSize screenSizeFor61InchAndiPhone12Later;
  154. @property(class, nonatomic, readonly) CGSize screenSizeFor61Inch;
  155. @property(class, nonatomic, readonly) CGSize screenSizeFor58Inch;
  156. @property(class, nonatomic, readonly) CGSize screenSizeFor55Inch;
  157. @property(class, nonatomic, readonly) CGSize screenSizeFor54Inch;
  158. @property(class, nonatomic, readonly) CGSize screenSizeFor47Inch;
  159. @property(class, nonatomic, readonly) CGSize screenSizeFor40Inch;
  160. @property(class, nonatomic, readonly) CGSize screenSizeFor35Inch;
  161. @property(class, nonatomic, readonly) CGFloat preferredLayoutAsSimilarScreenWidthForIPad;
  162. /// 用于获取 isNotchedScreen 设备的 insets,注意对于无 Home 键的新款 iPad 而言,它不一定有物理凹槽,但因为使用了 Home Indicator,所以它的 safeAreaInsets 也是非0。
  163. /// @NEW_DEVICE_CHECKER
  164. @property(class, nonatomic, readonly) UIEdgeInsets safeAreaInsetsForDeviceWithNotch;
  165. /// 判断当前设备是否高性能设备,只会判断一次,以后都直接读取结果,所以没有性能问题
  166. @property(class, nonatomic, readonly) BOOL isHighPerformanceDevice;
  167. /// 系统设置里是否开启了“放大显示-试图-放大”,支持放大模式的 iPhone 设备可在官方文档中查询 https://support.apple.com/zh-cn/guide/iphone/iphd6804774e/ios
  168. /// @NEW_DEVICE_CHECKER
  169. @property(class, nonatomic, readonly) BOOL isZoomedMode;
  170. /**
  171. 在 iPad 分屏模式下可获得实际运行区域的窗口大小,如需适配 iPad 分屏,建议用这个方法来代替 [UIScreen mainScreen].bounds.size
  172. @return 应用运行的窗口大小
  173. */
  174. @property(class, nonatomic, readonly) CGSize applicationSize;
  175. /**
  176. 静态的导航栏高度,在导航栏不可见时也会根据机型返回导航栏的固定高度
  177. */
  178. @property(class, nonatomic, readonly) CGFloat statusBarHeightConstant;
  179. @end
  180. @interface QMUIHelper (UIApplication)
  181. /**
  182. * 把App的主要window置灰,用于浮层弹出时,请注意要在适当时机调用`resetDimmedApplicationWindow`恢复到正常状态
  183. */
  184. + (void)dimmedApplicationWindow;
  185. /**
  186. * 恢复对App的主要window的置灰操作,与`dimmedApplicationWindow`成对调用
  187. */
  188. + (void)resetDimmedApplicationWindow;
  189. /**
  190. * 黑色的 StatusBarStyle,用于亮色背景
  191. * @note 在 iOS 13 以前 UIStatusBarStyleDefault 状态栏内容的颜色固定是黑色的,而在 iOS 13 UIStatusBarStyleDefault 会根据 user interface style 来决定状态栏的颜色,如果你需要一直黑色可以用 QMUIStatusBarStyleDarkContent 来代替以前 UIStatusBarStyleDefault 的写法
  192. * @return 在 iOS 13 以上返回 UIStatusBarStyleDarkContent,在 iOS 12 及以下返回 UIStatusBarStyleDefault
  193. */
  194. @property(class, nonatomic, readonly) UIStatusBarStyle statusBarStyleDarkContent;
  195. /**
  196. 在非 UIApplicationStateActive 的时机去设置 UIAppearance 可能引发第三方输入法 crash,因此提供这个方法判断当前是否可以更新 UIAppearance。
  197. 详情请见 https://github.com/Tencent/QMUI_iOS/issues/1281
  198. */
  199. @property(class, nonatomic, assign, readonly) BOOL canUpdateAppearance;
  200. @end
  201. @interface QMUIHelper (Animation)
  202. /**
  203. 在 animationBlock 里的操作完成之后会调用 completionBlock,常用于一些不提供 completionBlock 的系统动画操作。
  204. @param animationBlock 要进行的带动画的操作
  205. @param completionBlock 操作完成后的回调
  206. @note 注意 UIScrollView 系列的滚动无法使用这个方法。
  207. */
  208. + (void)executeAnimationBlock:(nonnull __attribute__((noescape)) void (^)(void))animationBlock completionBlock:(nullable __attribute__((noescape)) void (^)(void))completionBlock;
  209. @end
  210. NS_ASSUME_NONNULL_END