Categories

The following categories are available globally.

  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI)
    
    /**
     相当于 initWithFrame:CGRectMake(0, 0, size.width, size.height)
    
     @param size 初始化时的 size
     @return 初始化得到的实例
     */
    - (instancetype)qmui_initWithSize:(CGSize)size;
    
    /**
     将要设置的 frame 用 CGRectApplyAffineTransformWithAnchorPoint 处理后再设置
     */
    @property(nonatomic, assign) CGRect qmui_frameApplyTransform;
    
    /**
     在 iOS 11 及之后的版本,此属性将返回系统已有的 self.safeAreaInsets。在之前的版本此属性返回 UIEdgeInsetsZero
     */
    @property(nonatomic, assign, readonly) UIEdgeInsets qmui_safeAreaInsets;
    
    /**
     有修改过 tintColor,则不会再受 superview.tintColor 的影响
     */
    @property(nonatomic, assign, readonly) BOOL qmui_tintColorCustomized;
    
    /**
     移除当前所有 subviews
     */
    - (void)qmui_removeAllSubviews;
    
    /// 同 [UIView convertPoint:toView:],但支持在分属两个不同 window 的 view 之间进行坐标转换,也支持参数 view 直接传一个 window。
    - (CGPoint)qmui_convertPoint:(CGPoint)point toView:(nullable UIView *)view;
    
    /// 同 [UIView convertPoint:fromView:],但支持在分属两个不同 window 的 view 之间进行坐标转换,也支持参数 view 直接传一个 window。
    - (CGPoint)qmui_convertPoint:(CGPoint)point fromView:(nullable UIView *)view;
    
    /// 同 [UIView convertRect:toView:],但支持在分属两个不同 window 的 view 之间进行坐标转换,也支持参数 view 直接传一个 window。
    - (CGRect)qmui_convertRect:(CGRect)rect toView:(nullable UIView *)view;
    
    /// 同 [UIView convertRect:fromView:],但支持在分属两个不同 window 的 view 之间进行坐标转换,也支持参数 view 直接传一个 window。
    - (CGRect)qmui_convertRect:(CGRect)rect fromView:(nullable UIView *)view;
    
    + (void)qmui_animateWithAnimated:(BOOL)animated duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
    + (void)qmui_animateWithAnimated:(BOOL)animated duration:(NSTimeInterval)duration animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
    + (void)qmui_animateWithAnimated:(BOOL)animated duration:(NSTimeInterval)duration animations:(void (^ __nullable)(void))animations;
    + (void)qmui_animateWithAnimated:(BOOL)animated duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_Block)
    
    /**
     在 UIView 的 frame 变化前会调用这个 block,变化途径包括 setFrame:、setBounds:、setCenter:、setTransform:,你可以通过返回一个 rect 来达到修改 frame 的目的,最终执行 [super setFrame:] 时会使用这个 block 的返回值(除了 setTransform: 导致的 frame 变化)。
     @param view 当前的 view 本身,方便使用,省去 weak 操作
     @param followingFrame setFrame: 的参数 frame,也即即将被修改为的 rect 值
     @return 将会真正被使用的 frame 值
     @note 仅当 followingFrame 和 self.frame 值不相等时才会被调用
     */
    @property(nullable, nonatomic, copy) CGRect (^qmui_frameWillChangeBlock)(__kindof UIView *view, CGRect followingFrame);
    
    /**
     在 UIView 的 frame 变化后会调用这个 block,变化途径包括 setFrame:、setBounds:、setCenter:、setTransform:,可用于监听布局的变化,或者在不方便重写 layoutSubviews 时使用这个 block 代替。
     @param view 当前的 view 本身,方便使用,省去 weak 操作
     @param precedingFrame 修改前的 frame 值
     */
    @property(nullable, nonatomic, copy) void (^qmui_frameDidChangeBlock)(__kindof UIView *view, CGRect precedingFrame);
    
    /**
     在 UIView 的 layoutSubviews 调用后的下一个 runloop 调用。如果不放到下一个 runloop,直接就调用,会导致先于子类重写的 layoutSubviews 就调用,这样就无法获取到正确的 subviews 的布局。
     @param view 当前的 view 本身,方便使用,省去 weak 操作
     @note 如果某些 view 重写了 layoutSubviews 但没有调用 super,则这个 block 也不会被调用
     */
    @property(nullable, nonatomic, copy) void (^qmui_layoutSubviewsBlock)(__kindof UIView *view);
    
    /**
     当 tintColorDidChange 被调用的时候会调用这个 block,就不用重写方法了
     @param view 当前的 view 本身,方便使用,省去 weak 操作
     */
    @property(nullable, nonatomic, copy) void (^qmui_tintColorDidChangeBlock)(__kindof UIView *view);
    
    /**
     当 hitTest:withEvent: 被调用时会调用这个 block,就不用重写方法了
     @param point 事件产生的 point
     @param event 事件
     @param super 的返回结果
     */
    @property(nullable, nonatomic, copy) __kindof UIView * (^qmui_hitTestBlock)(CGPoint point, UIEvent *event, __kindof UIView *originalView);
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_ViewController)
    
    /**
     判断当前的 view 是否属于可视(可视的定义为已存在于 view 层级树里,或者在所处的 UIViewController 的 [viewWillAppear, viewWillDisappear) 生命周期之间)
     */
    @property(nonatomic, assign, readonly) BOOL qmui_visible;
    
    /**
     当前的 view 是否是某个 UIViewController.view
     */
    @property(nonatomic, assign) BOOL qmui_isControllerRootView;
    
    /**
     获取当前 view 所在的 UIViewController,会递归查找 superview,因此注意使用场景不要有过于频繁的调用
     */
    @property(nullable, nonatomic, weak, readonly) __kindof UIViewController *qmui_viewController;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_Runtime)
    
    /**
     *  判断当前类是否有重写某个指定的 UIView 的方法
     *  @param selector 要判断的方法
     *  @return YES 表示当前类重写了指定的方法,NO 表示没有重写,使用的是 UIView 默认的实现
     */
    - (BOOL)qmui_hasOverrideUIKitMethod:(SEL)selector;
    
    @end
  • UIView (QMUI_Border) 为 UIView 方便地显示某几个方向上的边框。

    系统的默认实现里,要为 UIView 加边框一般是通过 view.layer 来实现,view.layer 会给四条边都加上边框,如果你只想为其中某几条加上边框就很麻烦,于是 UIView (QMUI_Border) 提供了 qmui_borderPosition 来解决这个问题。

    Warning

    注意如果你需要为 UIView 四条边都加上边框,请使用系统默认的 view.layer 来实现,而不要用 UIView (QMUI_Border),会浪费资源,这也是为什么 QMUIViewBorderPosition 不提供一个 QMUIViewBorderPositionAll 枚举值的原因。
    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_Border)
  • 方便地将某个 UIView 截图并转成一个 UIImage,注意如果这个 UIView 本身做了 transform,也不会在截图上反映出来,截图始终都是原始 UIView 的截图。

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_Snapshotting)
  • 对 view.frame 操作的简便封装,注意 view 与 view 之间互相计算时,需要保证处于同一个坐标系内。

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_Layout)
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIView (CGAffineTransform)
    
    /// 获取当前 view 的 transform scale x
    @property(nonatomic, assign, readonly) CGFloat qmui_scaleX;
    
    /// 获取当前 view 的 transform scale y
    @property(nonatomic, assign, readonly) CGFloat qmui_scaleY;
    
    /// 获取当前 view 的 transform translation x
    @property(nonatomic, assign, readonly) CGFloat qmui_translationX;
    
    /// 获取当前 view 的 transform translation y
    @property(nonatomic, assign, readonly) CGFloat qmui_translationY;
    
    @end
  • Debug UIView 的时候用,对某个 view 的 subviews 都添加一个半透明的背景色,方面查看 view 的布局情况

    See more

    Declaration

    Objective-C

    @interface UIView (QMUI_Debug)
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITabBar (QMUI)
    
    /**
     UITabBar 的背景 view,可能显示磨砂、背景图,顶部有一部分溢出到 UITabBar 外。
     
     在 iOS 10 及以后是私有的 _UIBarBackground 类。
     
     在 iOS 9 及以前是私有的 _UITabBarBackgroundView 类。
     */
    @property(nonatomic, strong, readonly) UIView *qmui_backgroundView;
    
    /**
     qmui_backgroundView 内的 subview,用于显示顶部分隔线 shadowImage,注意这个 view 是溢出到 qmui_backgroundView 外的。若 shadowImage 为 [UIImage new],则这个 view 的高度为 0。
     */
    @property(nonatomic, strong, readonly) UIImageView *qmui_shadowImageView;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITabBarAppearance (QMUI)
    
    /**
     同时设置 stackedLayoutAppearance、inlineLayoutAppearance、compactInlineLayoutAppearance 三个状态下的 itemAppearance
     */
    - (void)qmui_applyItemAppearanceWithBlock:(void (^)(UITabBarItemAppearance *itemAppearance))block;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIBezierPath (QMUI)
    
    /**
     * 创建一条支持四个角的圆角值不相同的路径
     * @param rect 路径的rect
     * @param cornerRadius 圆角大小的数字,长度必须为4,顺序分别为[左上角、左下角、右下角、右上角]
     * @param lineWidth 描边的大小,如果不需要描边(例如path是用于fill而不是用于stroke),则填0
     */
    + (UIBezierPath *)qmui_bezierPathWithRoundedRect:(CGRect)rect cornerRadiusArray:(NSArray<NSNumber *> *)cornerRadius lineWidth:(CGFloat)lineWidth;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIWindow (QMUI)
    
    /**
     允许当前 window 接管 statusBar 的样式设置,默认为 YES。
     
     @note 经测试,- [UIViewController prefersStatusBarHidden]、- [UIViewController preferredStatusBarStyle]、- [UIViewController preferredStatusBarUpdateAnimation] 系列方法仅当该 viewController 所在的 UIWindow 符合以下条件时才能生效:
     1. window 处于最顶层,没有其他 window 遮挡
     2. iOS 10 及以后,window.frame 与 mainScreen.bounds 相等(origin、size 都应一模一样)
     因此当我们在某些情况下利用 UIWindow 去实现遮罩、浮层等效果时,会错误地导致原来的 window 内的 viewController 丢失了对 statusBar 的控制权(因为你新加的 window 满足了上文所有条件),为了避免这种情况,可以将你自己的 window.qmui_capturesStatusBarAppearance = NO,这样你的 window 就不会影响原 window 对 statusBar 的控制权。同理,如果你的 window 本身就不需要盖住整个屏幕,那就算你不设置 qmui_capturesStatusBarAppearance 也不会影响原 window 的表现。
     
     @warning 如果你自己创建的 window 不满足以上2点,那么就算 qmui_capturesStatusBarAppearance 为 YES,也无法得到 statusBar 的控制权。
     */
    @property(nonatomic, assign) BOOL qmui_capturesStatusBarAppearance;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITableViewCell (QMUI)
    
    /// 获取当前 cell 所在的 tableView,iOS 13 下在 init 时就可以获取到值,而 iOS 12 及以下只能在 cell 被塞给 tableView 后才能获取到值
    @property(nonatomic, weak, readonly, nullable) UITableView *qmui_tableView;
    
    /// 设置 cell 点击时的背景色,如果没有 selectedBackgroundView 会创建一个。
    /// @warning 请勿再使用 self.selectedBackgroundView.backgroundColor 修改,因为 QMUITheme 里会重新应用 qmui_selectedBackgroundColor,会覆盖 self.selectedBackgroundView.backgroundColor 的效果。
    @property(nonatomic, strong, nullable) UIColor *qmui_selectedBackgroundColor;
    
    /// setHighlighted:animated: 方法的回调 block
    @property(nonatomic, copy, nullable) void (^qmui_setHighlightedBlock)(BOOL highlighted, BOOL animated);
    
    /// setSelected:animated: 方法的回调 block
    @property(nonatomic, copy, nullable) void (^qmui_setSelectedBlock)(BOOL selected, BOOL animated);
    
    /// 获取当前 cell 的 accessoryView,优先级分别是:编辑状态下的 editingAccessoryView -> 编辑状态下的系统自己的 accessoryView -> 普通状态下的自定义 accessoryView -> 普通状态下系统自己的 accessoryView
    @property(nonatomic, strong, readonly, nullable) __kindof UIView *qmui_accessoryView;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITableViewCell (QMUI_Styled)
    
    /// 按照 QMUI 配置表的值来将 cell 设置为全局统一的样式
    - (void)qmui_styledAsQMUITableViewCell;
    
    @property(nonatomic, strong, readonly, nullable) UIColor *qmui_styledTextLabelColor;
    @property(nonatomic, strong, readonly, nullable) UIColor *qmui_styledDetailTextLabelColor;
    @property(nonatomic, strong, readonly, nullable) UIColor *qmui_styledBackgroundColor;
    @property(nonatomic, strong, readonly, nullable) UIColor *qmui_styledSelectedBackgroundColor;
    @property(nonatomic, strong, readonly, nullable) UIColor *qmui_styledWarningBackgroundColor;
    @end
  • 提供更丰富的接口来修改 UISearchBar 的样式,注意大部分接口都同时支持配置表和 UIAppearance,如果有使用配置表并且该项的值不为 nil,则以配置表的值为准。

    See more

    Declaration

    Objective-C

    @interface UISearchBar (QMUI)
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSAttributedString (QMUI)
    
    /**
     *  按照中文 2 个字符、英文 1 个字符的方式来计算文本长度
     */
    - (NSUInteger)qmui_lengthWhenCountingNonASCIICharacterAsTwo;
    
    /**
     * @brief 创建一个包含图片的 attributedString
     * @param image 要用的图片
     */
    + (instancetype)qmui_attributedStringWithImage:(UIImage *)image;
    
    /**
     * @brief 创建一个包含图片的 attributedString
     * @param image 要用的图片
     * @param offset 图片相对基线的垂直偏移(当 offset > 0 时,图片会向上偏移)
     * @param leftMargin 图片距离左侧内容的间距
     * @param rightMargin 图片距离右侧内容的间距
     * @note leftMargin 和 rightMargin 必须大于或等于 0
     */
    + (instancetype)qmui_attributedStringWithImage:(UIImage *)image baselineOffset:(CGFloat)offset leftMargin:(CGFloat)leftMargin rightMargin:(CGFloat)rightMargin;
    
    /**
     * @brief 创建一个用来占位的空白 attributedString
     * @param width 空白占位符的宽度
     */
    + (instancetype)qmui_attributedStringWithFixedSpace:(CGFloat)width;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSCharacterSet (QMUI)
    
    /**
     也即在系统的 URLQueryAllowedCharacterSet 基础上去掉“#&=”这3个字符,专用于 URL query 里来源于用户输入的 value,避免服务器解析出现异常。
     */
    @property (class, readonly, copy) NSCharacterSet *qmui_URLUserInputQueryAllowedCharacterSet;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UISwitch (QMUI)
    
    /// 用于设置 UISwitch 关闭时的背景色(除了圆点外的其他颜色)
    @property(nonatomic, strong) UIColor *qmui_offTintColor;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITextView (QMUI)
    
    /**
     *  convert UITextRange to NSRange, for example, [self qmui_convertNSRangeFromUITextRange:self.markedTextRange]
     */
    - (NSRange)qmui_convertNSRangeFromUITextRange:(UITextRange *)textRange;
    
    /**
     *  convert NSRange to UITextRange
     *  @return return nil if range is invalidate.
     */
    - (nullable UITextRange *)qmui_convertUITextRangeFromNSRange:(NSRange)range;
    
    /**
     *  设置 text 会让 selectedTextRange 跳到最后一个字符,导致在中间修改文字后光标会跳到末尾,所以设置前要保存一下,设置后恢复过来
     */
    - (void)qmui_setTextKeepingSelectedRange:(NSString *)text;
    
    /**
     *  设置 attributedText 会让 selectedTextRange 跳到最后一个字符,导致在中间修改文字后光标会跳到末尾,所以设置前要保存一下,设置后恢复过来
     */
    - (void)qmui_setAttributedTextKeepingSelectedRange:(NSAttributedString *)attributedText;
    
    /**
     [UITextView scrollRangeToVisible:] 并不会考虑 textContainerInset.bottom,所以使用这个方法来代替
    
     @param range 要滚动到的文字区域,如果 range 非法则什么都不做
     */
    - (void)qmui_scrollRangeToVisible:(NSRange)range;
    
    /**
     * 将光标滚到可视区域
     */
    - (void)qmui_scrollCaretVisibleAnimated:(BOOL)animated;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSObject (QMUITextInput_Private)
    
    /// 内部使用,用于标志业务自己修改了 keyboardAppearance 的情况
    @property(nonatomic, assign) BOOL qmui_hasCustomizedKeyboardAppearance;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITabBarItem (QMUI)
    
    /**
     *  双击 tabBarItem 时的回调,默认为 nil。
     *  @arg tabBarItem 被双击的 UITabBarItem
     *  @arg index      被双击的 UITabBarItem 的序号
     */
    @property(nonatomic, copy) void (^qmui_doubleTapBlock)(UITabBarItem *tabBarItem, NSInteger index);
    
    /**
     * 获取一个UITabBarItem内显示图标的UIImageView,如果找不到则返回nil
     * @warning 需要对nil的返回值做保护
     */
    - (UIImageView *)qmui_imageView;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIActivityIndicatorView (QMUI)
    
    /**
     * 创建一个指定大小的UIActivityIndicatorView
     *
     * 系统的UIActivityIndicatorView尺寸是由UIActivityIndicatorViewStyle决定的,固定不变。因此创建后通过CGAffineTransformMakeScale将其缩放到指定大小。self.frame获取的值也是缩放后的值,不影响布局。
     *
     * @param style UIActivityIndicatorViewStyle
     * @param size  UIActivityIndicatorView的大小
     *
     * @return UIActivityIndicatorView对象
     */
    - (instancetype)initWithActivityIndicatorStyle:(UIActivityIndicatorViewStyle)style size:(CGSize)size;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIBarItem (QMUI)
    
    /**
     获取 UIBarItem(UIBarButtonItem、UITabBarItem) 内部的 view,通常对于 navigationItem 而言,需要在设置了 navigationItem 后并且在 navigationBar 可见时(例如 viewDidAppear: 及之后)获取 UIBarButtonItem.qmui_view 才有值。
     
     @return 当 UIBarButtonItem 作为 navigationItem 使用时,iOS 10 及以前返回 UINavigationButton,iOS 11 及以后返回 _UIButtonBarButton;当作为 toolbarItem 使用时,iOS 10 及以前返回 UIToolbarButton,iOS 11 及以后返回 _UIButtonBarButton。对于 UITabBarItem,不管任何 iOS 版本均返回 UITabBarButton。
     
     @note 可以通过 qmui_viewDidSetBlock 监听 qmui_view 值的变化,从而无需等待 viewDidAppear: 之类的时机。
     
     @warning 仅对 UIBarButtonItem、UITabBarItem 有效
     */
    @property(nullable, nonatomic, weak, readonly) UIView *qmui_view;
    
    /**
     当 item 内的 view 生成后就会调用这个 block。
     
     @note 该方法的本质是系统的 setView:/setCustomView: 被调用时就会调用,但系统在横竖屏旋转时也会再次走到 setView:(即便此时 view 的实例并没有发生变化),所以 QMUI 对这种情况做了屏蔽,以保证这个 block 对于同一个 view 实例只会被调用一次。
     
     @warning 仅对 UIBarButtonItem、UITabBarItem 有效
     */
    @property(nullable, nonatomic, copy) void (^qmui_viewDidSetBlock)(__kindof UIBarItem *item,  UIView * _Nullable view);
    
    /**
     当 item 内的 view 的 layoutSubviews 被调用后就会调用这个 block,如果某些需求需要依赖于 subviews 的位置,则使用这个 block。如果只是依赖于 item 的 view 的 frame 变化,则可以使用 qmui_viewLayoutDidChangeBlock。
     
     @warning 仅对 UIBarButtonItem、UITabBarItem 有效
     */
    @property(nullable, nonatomic, copy) void (^qmui_viewDidLayoutSubviewsBlock)(__kindof UIBarItem *item, UIView * _Nullable view);
    
    /**
     当 item 内的 view 的 frame 发生变化时就会调用这个 block。
     
     @warning 仅对 UIBarButtonItem、UITabBarItem 有效
     */
    @property(nullable, nonatomic, copy) void (^qmui_viewLayoutDidChangeBlock)(__kindof UIBarItem *item, UIView * _Nullable view);
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSURL (QMUI)
    
    /**
     *  获取当前 query 的参数列表。
     *
     *  @return query 参数列表,以字典返回。如果 absoluteString 为 nil 则返回 nil
     */
    @property(nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *qmui_queryItems;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSMutableParagraphStyle (QMUI)
    
    /**
     *  快速创建一个NSMutableParagraphStyle,等同于`qmui_paragraphStyleWithLineHeight:lineBreakMode:NSLineBreakByWordWrapping textAlignment:NSTextAlignmentLeft`
     *  @param  lineHeight      行高
     *  @return 一个NSMutableParagraphStyle对象
     */
    + (instancetype)qmui_paragraphStyleWithLineHeight:(CGFloat)lineHeight;
    
    /**
     *  快速创建一个NSMutableParagraphStyle,等同于`qmui_paragraphStyleWithLineHeight:lineBreakMode:textAlignment:NSTextAlignmentLeft`
     *  @param  lineHeight      行高
     *  @param  lineBreakMode   换行模式
     *  @return 一个NSMutableParagraphStyle对象
     */
    + (instancetype)qmui_paragraphStyleWithLineHeight:(CGFloat)lineHeight lineBreakMode:(NSLineBreakMode)lineBreakMode;
    
    /**
     *  快速创建一个NSMutableParagraphStyle
     *  @param  lineHeight      行高
     *  @param  lineBreakMode   换行模式
     *  @param  textAlignment   文本对齐方式
     *  @return 一个NSMutableParagraphStyle对象
     */
    + (instancetype)qmui_paragraphStyleWithLineHeight:(CGFloat)lineHeight lineBreakMode:(NSLineBreakMode)lineBreakMode textAlignment:(NSTextAlignment)textAlignment;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSObject (QMUI)
    
    /**
     判断当前类是否有重写某个父类的指定方法
     
     @param selector 要判断的方法
     @param superclass 要比较的父类,必须是当前类的某个 superclass
     @return YES 表示子类有重写了父类方法,NO 表示没有重写(异常情况也返回 NO,例如当前类与指定的类并非父子关系、父类本身也无法响应指定的方法)
     */
    - (BOOL)qmui_hasOverrideMethod:(SEL)selector ofSuperclass:(Class)superclass;
    
    /**
     判断指定的类是否有重写某个父类的指定方法
     
     @param selector 要判断的方法
     @param superclass 要比较的父类,必须是当前类的某个 superclass
     @return YES 表示子类有重写了父类方法,NO 表示没有重写(异常情况也返回 NO,例如当前类与指定的类并非父子关系、父类本身也无法响应指定的方法)
     */
    + (BOOL)qmui_hasOverrideMethod:(SEL)selector forClass:(Class)aClass ofSuperclass:(Class)superclass;
    
    /**
     对 super 发送消息
     
     @param aSelector 要发送的消息
     @return 消息执行后的结果
     @link http://stackoverflow.com/questions/14635024/using-objc-msgsendsuper-to-invoke-a-class-method @/link
     */
    - (nullable id)qmui_performSelectorToSuperclass:(SEL)aSelector;
    
    /**
     对 super 发送消息
     
     @param aSelector 要发送的消息
     @param object 作为参数传过去
     @return 消息执行后的结果
     @link http://stackoverflow.com/questions/14635024/using-objc-msgsendsuper-to-invoke-a-class-method @/link
     */
    - (nullable id)qmui_performSelectorToSuperclass:(SEL)aSelector withObject:(nullable id)object;
    
    /**
     *  调用一个无参数、返回值类型为非对象的 selector。如果返回值类型为对象,请直接使用系统的 performSelector: 方法。
     *  @param selector 要被调用的方法名
     *  @param returnValue selector 的返回值的指针地址,请先定义一个变量再将其指针地址传进来,例如 &result
     *
     *  @code
     *  CGFloat alpha;
     *  [view qmui_performSelector:@selector(alpha) withPrimitiveReturnValue:&alpha];
     *  @endcode
     */
    - (void)qmui_performSelector:(SEL)selector withPrimitiveReturnValue:(nullable void *)returnValue;
    
    /**
     *  调用一个带参数的 selector,参数类型支持对象和非对象,也没有数量限制。返回值为对象或者 void。
     *  @param selector 要被调用的方法名
     *  @param firstArgument 参数列表,请传参数的指针地址,支持多个参数
     *  @return 方法的返回值,如果该方法返回类型为 void,则会返回 nil,如果返回类型为对象,则返回该对象。
     *
     *  @code
     *  id target = xxx;
     *  SEL action = xxx;
     *  UIControlEvents events = xxx;
     *  [control qmui_performSelector:@selector(addTarget:action:forControlEvents:) withArguments:&target, &action, &events, nil];
     *  @endcode
     */
    - (nullable id)qmui_performSelector:(SEL)selector withArguments:(nullable void *)firstArgument, ...;
    
    /**
     *  调用一个返回值类型为非对象且带参数的 selector,参数类型支持对象和非对象,也没有数量限制。
     *
     *  @param selector 要被调用的方法名
     *  @param returnValue selector 的返回值的指针地址
     *  @param firstArgument 参数列表,请传参数的指针地址,支持多个参数
     *
     *  @code
     *  CGPoint point = xxx;
     *  UIEvent *event = xxx;
     *  BOOL isInside;
     *  [view qmui_performSelector:@selector(pointInside:withEvent:) withPrimitiveReturnValue:&isInside arguments:&point, &event, nil];
     *  @endcode
     */
    - (void)qmui_performSelector:(SEL)selector withPrimitiveReturnValue:(nullable void *)returnValue arguments:(nullable void *)firstArgument, ...;
    
    
    /**
     使用 block 遍历指定 class 的所有成员变量(也即 _xxx 那种),不包含 property 对应的 _property 成员变量,也不包含 superclasses 里定义的变量
     
     @param block 用于遍历的 block
     */
    - (void)qmui_enumrateIvarsUsingBlock:(void (^)(Ivar ivar, NSString *ivarDescription))block;
    
    /**
     使用 block 遍历指定 class 的所有成员变量(也即 _xxx 那种),不包含 property 对应的 _property 成员变量
     
     @param aClass 指定的 class
     @param includingInherited 是否要包含由继承链带过来的 ivars
     @param block  用于遍历的 block
     */
    + (void)qmui_enumrateIvarsOfClass:(Class)aClass includingInherited:(BOOL)includingInherited usingBlock:(void (^)(Ivar ivar, NSString *ivarDescription))block;
    
    /**
     使用 block 遍历指定 class 的所有属性,不包含 superclasses 里定义的 property
     
     @param block 用于遍历的 block,如果要获取 property 的信息,推荐用 QMUIPropertyDescriptor。
     */
    - (void)qmui_enumratePropertiesUsingBlock:(void (^)(objc_property_t property, NSString *propertyName))block;
    
    /**
     使用 block 遍历指定 class 的所有属性
     
     @param aClass 指定的 class
     @param includingInherited 是否要包含由继承链带过来的 property
     @param block 用于遍历的 block,如果要获取 property 的信息,推荐用 QMUIPropertyDescriptor。
     @see https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW1
     */
    + (void)qmui_enumratePropertiesOfClass:(Class)aClass includingInherited:(BOOL)includingInherited usingBlock:(void (^)(objc_property_t property, NSString *propertyName))block;
    
    /**
     使用 block 遍历当前实例的所有方法,不包含 superclasses 里定义的 method
     */
    - (void)qmui_enumrateInstanceMethodsUsingBlock:(void (^)(Method method, SEL selector))block;
    
    /**
     使用 block 遍历指定的某个类的实例方法
     @param aClass   指定的 class
     @param includingInherited 是否要包含由继承链带过来的 method
     @param block    用于遍历的 block
     */
    + (void)qmui_enumrateInstanceMethodsOfClass:(Class)aClass includingInherited:(BOOL)includingInherited usingBlock:(void (^)(Method method, SEL selector))block;
    
    /**
     遍历某个 protocol 里的所有方法
     
     @param protocol 要遍历的 protocol,例如 \@protocol(xxx)
     @param block 遍历过程中调用的 block
     */
    + (void)qmui_enumerateProtocolMethods:(Protocol *)protocol usingBlock:(void (^)(SEL selector))block;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSObject (QMUI_KeyValueCoding)
    
    /**
     iOS 13 下系统禁止通过 KVC 访问私有 API,因此提供这种方式在遇到 access prohibited 的异常时可以取代 valueForKey: 使用。
     
     对 iOS 12 及以下的版本,等价于 valueForKey:。
     
     @note QMUI 提供2种方式兼容系统的 access prohibited 异常:
     1. 通过将配置表的 IgnoreKVCAccessProhibited 置为 YES 来全局屏蔽系统的异常警告,代码中依然正常使用系统的 valueForKey:、setValue:forKey:,当开启后再遇到 access prohibited 异常时,将会用 QMUIWarnLog 来提醒,不再中断 App 的运行,这是首选推荐方案。
     2. 使用 qmui_valueForKey:、qmui_setValue:forKey: 代替系统的 valueForKey:、setValue:forKey:,适用于不希望全局屏蔽,只针对某个局部代码自己处理的场景。
     
     @link https://github.com/Tencent/QMUI_iOS/issues/617
     
     @param key ivar 属性名,支持下划线或不带下划线
     @return key 对应的 value,如果该 key 原本是非对象的值,会被用 NSNumber、NSValue 包裹后返回
     */
    - (nullable id)qmui_valueForKey:(NSString *)key;
    
    /**
     iOS 13 下系统禁止通过 KVC 访问私有 API,因此提供这种方式在遇到 access prohibited 的异常时可以取代 setValue:forKey: 使用。
     
     对 iOS 12 及以下的版本,等价于 setValue:forKey:。
     
     @note QMUI 提供2种方式兼容系统的 access prohibited 异常:
     1. 通过将配置表的 IgnoreKVCAccessProhibited 置为 YES 来全局屏蔽系统的异常警告,代码中依然正常使用系统的 valueForKey:、setValue:forKey:,当开启后再遇到 access prohibited 异常时,将会用 QMUIWarnLog 来提醒,不再中断 App 的运行,这是首选推荐方案。
     2. 使用 qmui_valueForKey:、qmui_setValue:forKey: 代替系统的 valueForKey:、setValue:forKey:,适用于不希望全局屏蔽,只针对某个局部代码自己处理的场景。
     
     @link https://github.com/Tencent/QMUI_iOS/issues/617
     
     @param key ivar 属性名,支持下划线或不带下划线
     @return key 对应的 value,如果该 key 原本是非对象的值,会被用 NSNumber、NSValue 包裹后返回
     */
    - (void)qmui_setValue:(nullable id)value forKey:(NSString *)key;
    
    /**
     检查给定的 key 是否可以用于当前对象的 valueForKey: 调用。
     
     @note 这是针对 valueForKey: 内部查找 key 的逻辑的精简版,去掉了一些不常用的,如果按精简版查找不到,会返回 NO(但按完整版可能是能查找到的),避免抛出异常。文档描述的查找方法完整版请查看 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/SearchImplementation.html
     */
    - (BOOL)qmui_canGetValueForKey:(NSString *)key;
    
    /**
    检查给定的 key 是否可以用于当前对象的 setValue:forKey: 调用。
    
    @note 对于 setter 而言这就是完整版的检查流程,可核对文档 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/SearchImplementation.html
    */
    - (BOOL)qmui_canSetValueForKey:(NSString *)key;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSObject (QMUI_DataBind)
    
    /**
     给对象绑定上另一个对象以供后续取出使用,如果 object 传入 nil 则会清除该 key 之前绑定的对象
     
     @attention 被绑定的对象会被 strong 强引用
     @note 内部是使用 objc_setAssociatedObject / objc_getAssociatedObject 来实现
     
     @code
     - (UITableViewCell *)cellForIndexPath:(NSIndexPath *)indexPath {
     // 1)在这里给 button 绑定上 indexPath 对象
     [cell qmui_bindObject:indexPath forKey:@"indexPath"];
     }
     
     - (void)didTapButton:(UIButton *)button {
     // 2)在这里取出被点击的 button 的 indexPath 对象
     NSIndexPath *indexPathTapped = [button qmui_getBoundObjectForKey:@"indexPath"];
     }
     @endcode
     */
    - (void)qmui_bindObject:(nullable id)object forKey:(NSString *)key;
    
    /**
     给对象绑定上另一个对象以供后续取出使用,但相比于 qmui_bindObject:forKey:,该方法不会 strong 强引用传入的 object
     */
    - (void)qmui_bindObjectWeakly:(nullable id)object forKey:(NSString *)key;
    
    /**
     取出之前使用 bind 方法绑定的对象
     */
    - (nullable id)qmui_getBoundObjectForKey:(NSString *)key;
    
    /**
     给对象绑定上一个 double 值以供后续取出使用
     */
    - (void)qmui_bindDouble:(double)doubleValue forKey:(NSString *)key;
    
    /**
     取出之前用 bindDouble:forKey: 绑定的值
     */
    - (double)qmui_getBoundDoubleForKey:(NSString *)key;
    
    /**
     给对象绑定上一个 BOOL 值以供后续取出使用
     */
    - (void)qmui_bindBOOL:(BOOL)boolValue forKey:(NSString *)key;
    
    /**
     取出之前用 bindBOOL:forKey: 绑定的值
     */
    - (BOOL)qmui_getBoundBOOLForKey:(NSString *)key;
    
    /**
     给对象绑定上一个 long 值以供后续取出使用
     */
    - (void)qmui_bindLong:(long)longValue forKey:(NSString *)key;
    
    /**
     取出之前用 bindLong:forKey: 绑定的值
     */
    - (long)qmui_getBoundLongForKey:(NSString *)key;
    
    /**
     移除之前使用 bind 方法绑定的对象
     */
    - (void)qmui_clearBindingForKey:(NSString *)key;
    
    /**
     移除之前使用 bind 方法绑定的所有对象
     */
    - (void)qmui_clearAllBinding;
    
    /**
     返回当前有绑定对象存在的所有的 key 的数组,如果不存在任何 key,则返回一个空数组
     @note 数组中元素的顺序是随机的
     */
    - (NSArray<NSString *> *)qmui_allBindingKeys;
    
    /**
     返回是否设置了某个 key
     */
    - (BOOL)qmui_hasBindingKey:(NSString *)key;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSObject (QMUI_Debug)
    
    /// 获取当前对象的所有 @property、方法,父类的方法也会分别列出
    - (NSString *)qmui_methodList;
    
    /// 获取当前对象的所有 @property、方法,不包含父类的
    - (NSString *)qmui_shortMethodList;
    
    /// 获取当前对象的所有 Ivar 变量
    - (NSString *)qmui_ivarList;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSThread (QMUI_KVC)
    
    /// 是否将当前线程标记为忽略系统的 KVC access prohibited 警告,默认为 NO,当开启后,NSException 将不会再抛出 access prohibited 异常
    /// @see BeginIgnoreUIKVCAccessProhibited、EndIgnoreUIKVCAccessProhibited
    @property(nonatomic, assign) BOOL qmui_shouldIgnoreUIKVCAccessProhibited;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIFont (QMUI)
    
    /**
     *  返回系统字体的细体
     *
     *  @param fontSize 字体大小
     *
     *  @return 变细的系统字体的 UIFont 对象
     */
    + (UIFont *)qmui_lightSystemFontOfSize:(CGFloat)fontSize;
    
    /**
     *  根据需要生成一个 UIFont 对象并返回
     *  @param size     字号大小
     *  @param weight   字体粗细
     *  @param italic   是否斜体
     */
    + (UIFont *)qmui_systemFontOfSize:(CGFloat)size
                               weight:(QMUIFontWeight)weight
                               italic:(BOOL)italic;
    
    /**
     *  根据需要生成一个支持响应动态字体大小调整的 UIFont 对象并返回
     *  @param  size    字号大小
     *  @param  weight  字重
     *  @param  italic  是否斜体
     *  @return         支持响应动态字体大小调整的 UIFont 对象
     */
    + (UIFont *)qmui_dynamicSystemFontOfSize:(CGFloat)size
                                      weight:(QMUIFontWeight)weight
                                      italic:(BOOL)italic;
    
    /**
     *  返回支持动态字体的UIFont,支持定义最小和最大字号
     *
     *  @param pointSize        默认的size
     *  @param upperLimitSize   最大的字号限制
     *  @param lowerLimitSize   最小的字号显示
     *  @param weight           字重
     *  @param italic           是否斜体
     *
     *  @return                 支持响应动态字体大小调整的 UIFont 对象
     */
    + (UIFont *)qmui_dynamicSystemFontOfSize:(CGFloat)pointSize
                              upperLimitSize:(CGFloat)upperLimitSize
                              lowerLimitSize:(CGFloat)lowerLimitSize
                                      weight:(QMUIFontWeight)weight
                                      italic:(BOOL)italic;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSMethodSignature (QMUI)
    
    /**
     返回一个避免 crash 的方法签名,用于重写 methodSignatureForSelector: 时作为垫底的 return 方案
     */
    @property(nullable, class, nonatomic, readonly) NSMethodSignature *qmui_avoidExceptionSignature;
    
    /**
     以 NSString 格式返回当前 NSMethodSignature 的 typeEncoding,例如 v@:
     */
    @property(nullable, nonatomic, copy, readonly) NSString *qmui_typeString;
    
    /**
     以 const char 格式返回当前 NSMethodSignature 的 typeEncoding,例如 v@:
     */
    @property(nullable, nonatomic, readonly) const char *qmui_typeEncoding;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSPointerArray (QMUI)
    
    - (NSUInteger)qmui_indexOfPointer:(nullable void *)pointer;
    - (BOOL)qmui_containsPointer:(nullable void *)pointer;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSString (QMUI)
    
    /// 将字符串按一个一个字符拆成数组,类似 JavaScript 里的 split(""),如果多个空格,则每个空格也会当成一个 item
    @property(nullable, readonly, copy) NSArray<NSString *> *qmui_toArray;
    
    /// 将字符串按一个一个字符拆成数组,类似 JavaScript 里的 split(""),但会自动过滤掉空白字符
    @property(nullable, readonly, copy) NSArray<NSString *> *qmui_toTrimmedArray;
    
    /// 去掉头尾的空白字符
    @property(readonly, copy) NSString *qmui_trim;
    
    /// 去掉整段文字内的所有空白字符(包括换行符)
    @property(readonly, copy) NSString *qmui_trimAllWhiteSpace;
    
    /// 将文字中的换行符替换为空格
    @property(readonly, copy) NSString *qmui_trimLineBreakCharacter;
    
    /// 把该字符串转换为对应的 md5
    @property(readonly, copy) NSString *qmui_md5;
    
    /// 返回一个符合 query value 要求的编码后的字符串,例如&、#、=等字符均会被变为 %xxx 的编码
    /// @see `NSCharacterSet (QMUI) qmui_URLUserInputQueryAllowedCharacterSet`
    @property(nullable, readonly, copy) NSString *qmui_stringByEncodingUserInputQuery;
    
    /// 把当前文本的第一个字符改为大写,其他的字符保持不变,例如 backgroundView.qmui_capitalizedString -> BackgroundView(系统的 capitalizedString 会变成 Backgroundview)
    @property(nullable, readonly, copy) NSString *qmui_capitalizedString;
    
    /**
     * 用正则表达式匹配的方式去除字符串里一些特殊字符,避免UI上的展示问题
     * @link http://www.croton.su/en/uniblock/Diacriticals.html @/link
     */
    @property(nullable, readonly, copy) NSString *qmui_removeMagicalChar;
    
    /**
     *  按照中文 2 个字符、英文 1 个字符的方式来计算文本长度
     */
    @property(readonly) NSUInteger qmui_lengthWhenCountingNonASCIICharacterAsTwo;
    
    /**
     *  将字符串从指定的 index 开始裁剪到结尾,裁剪时会避免将 emoji 等 "character sequences" 拆散(一个 emoji 表情占用1-4个长度的字符)。
     *
     *  例如对于字符串“😊😞”,它的长度为4,若调用 [string qmui_substringAvoidBreakingUpCharacterSequencesFromIndex:1],将返回“😊😞”。
     *  若调用系统的 [string substringFromIndex:1],将返回“?😞”。(?表示乱码,因为第一个 emoji 表情被从中间裁开了)。
     *
     *  @param index 要从哪个 index 开始裁剪文字
     *  @param lessValue 要按小的长度取,还是按大的长度取
     *  @param countingNonASCIICharacterAsTwo 是否按照 英文 1 个字符长度、中文 2 个字符长度的方式来裁剪
     *  @return 裁剪完的字符
     */
    - (NSString *)qmui_substringAvoidBreakingUpCharacterSequencesFromIndex:(NSUInteger)index lessValue:(BOOL)lessValue countingNonASCIICharacterAsTwo:(BOOL)countingNonASCIICharacterAsTwo;
    
    /**
     *  相当于 `qmui_substringAvoidBreakingUpCharacterSequencesFromIndex: lessValue:YES` countingNonASCIICharacterAsTwo:NO
     *  @see qmui_substringAvoidBreakingUpCharacterSequencesFromIndex:lessValue:countingNonASCIICharacterAsTwo:
     */
    - (NSString *)qmui_substringAvoidBreakingUpCharacterSequencesFromIndex:(NSUInteger)index;
    
    /**
     *  将字符串从开头裁剪到指定的 index,裁剪时会避免将 emoji 等 "character sequences" 拆散(一个 emoji 表情占用1-4个长度的字符)。
     *
     *  例如对于字符串“😊😞”,它的长度为4,若调用 [string qmui_substringAvoidBreakingUpCharacterSequencesToIndex:1 lessValue:NO countingNonASCIICharacterAsTwo:NO],将返回“😊”。
     *  若调用系统的 [string substringToIndex:1],将返回“?”。(?表示乱码,因为第一个 emoji 表情被从中间裁开了)。
     *
     *  @param index 要裁剪到哪个 index
     *  @param lessValue 裁剪时若遇到“character sequences”,是向下取整还是向上取整。
     *  @param countingNonASCIICharacterAsTwo 是否按照 英文 1 个字符长度、中文 2 个字符长度的方式来裁剪
     *  @return 裁剪完的字符
     */
    - (NSString *)qmui_substringAvoidBreakingUpCharacterSequencesToIndex:(NSUInteger)index lessValue:(BOOL)lessValue countingNonASCIICharacterAsTwo:(BOOL)countingNonASCIICharacterAsTwo;
    
    /**
     *  相当于 `qmui_substringAvoidBreakingUpCharacterSequencesToIndex:lessValue:YES` countingNonASCIICharacterAsTwo:NO
     *  @see qmui_substringAvoidBreakingUpCharacterSequencesToIndex:lessValue:countingNonASCIICharacterAsTwo:
     */
    - (NSString *)qmui_substringAvoidBreakingUpCharacterSequencesToIndex:(NSUInteger)index;
    
    /**
     *  将字符串里指定 range 的子字符串裁剪出来,会避免将 emoji 等 "character sequences" 拆散(一个 emoji 表情占用1-4个长度的字符)。
     *
     *  例如对于字符串“😊😞”,它的长度为4,在 lessValue 模式下,裁剪 (0, 1) 得到的是空字符串,裁剪 (0, 2) 得到的是“😊”。
     *  在非 lessValue 模式下,裁剪 (0, 1) 或 (0, 2),得到的都是“😊”。
     *
     *  @param range 要裁剪的文字位置
     *  @param lessValue 裁剪时若遇到“character sequences”,是向下取整还是向上取整。
     *  @param countingNonASCIICharacterAsTwo 是否按照 英文 1 个字符长度、中文 2 个字符长度的方式来裁剪
     *  @return 裁剪完的字符
     */
    - (NSString *)qmui_substringAvoidBreakingUpCharacterSequencesWithRange:(NSRange)range lessValue:(BOOL)lessValue countingNonASCIICharacterAsTwo:(BOOL)countingNonASCIICharacterAsTwo;
    
    /**
     *  相当于 `qmui_substringAvoidBreakingUpCharacterSequencesWithRange:lessValue:YES` countingNonASCIICharacterAsTwo:NO
     *  @see qmui_substringAvoidBreakingUpCharacterSequencesWithRange:lessValue:countingNonASCIICharacterAsTwo:
     */
    - (NSString *)qmui_substringAvoidBreakingUpCharacterSequencesWithRange:(NSRange)range;
    
    /**
     *  移除指定位置的字符,可兼容emoji表情的情况(一个emoji表情占1-4个length)
     *  @param index 要删除的位置
     */
    - (NSString *)qmui_stringByRemoveCharacterAtIndex:(NSUInteger)index;
    
    /**
     *  移除最后一个字符,可兼容emoji表情的情况(一个emoji表情占1-4个length)
     *  @see `qmui_stringByRemoveCharacterAtIndex:`
     */
    - (NSString *)qmui_stringByRemoveLastCharacter;
    
    /**
     用正则表达式匹配字符串,将匹配到的第一个结果返回,大小写不敏感
    
     @param pattern 正则表达式
     @return 匹配到的第一个结果,如果没有匹配成功则返回 nil
     */
    - (NSString *)qmui_stringMatchedByPattern:(NSString *)pattern;
    
    /**
     *  用正则表达式匹配字符串并将其替换为指定的另一个字符串,大小写不敏感
     *  @param pattern 正则表达式
     *  @param replacement 要替换为的字符串
     *  @return 最终替换后的完整字符串,如果正则表达式匹配不成功则返回原字符串
     */
    - (NSString *)qmui_stringByReplacingPattern:(NSString *)pattern withString:(NSString *)replacement;
    
    /// 把某个十进制数字转换成十六进制的数字的字符串,例如“10”->“A”
    + (NSString *)qmui_hexStringWithInteger:(NSInteger)integer;
    
    /// 把参数列表拼接成一个字符串并返回,相当于用另一种语法来代替 [NSString stringWithFormat:]
    + (NSString *)qmui_stringByConcat:(id)firstArgv, ...;
    
    /**
     * 将秒数转换为同时包含分钟和秒数的格式的字符串,例如 100->"01:40"
     */
    + (NSString *)qmui_timeStringWithMinsAndSecsFromSecs:(double)seconds;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSString (QMUI_StringFormat)
    
    + (instancetype)qmui_stringWithNSInteger:(NSInteger)integerValue;
    + (instancetype)qmui_stringWithCGFloat:(CGFloat)floatValue;
    + (instancetype)qmui_stringWithCGFloat:(CGFloat)floatValue decimal:(NSUInteger)decimal;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UINavigationController (QMUI) <UIGestureRecognizerDelegate>
    
    /// 是否在 push 的过程中
    @property(nonatomic, readonly) BOOL qmui_isPushing;
    
    /// 是否在 pop 的过程中,包括手势、以及代码触发的 pop
    @property(nonatomic, readonly) BOOL qmui_isPopping;
    
    /// 获取顶部的 ViewController,相比于系统的方法,这个方法能获取到 pop 的转场过程中顶部还没有完全消失的 ViewController (请注意:这种情况下,获取到的 topViewController 已经不在栈内)
    @property(nullable, nonatomic, readonly) UIViewController *qmui_topViewController;
    
    /// 获取<b>rootViewController</b>
    @property(nullable, nonatomic, readonly) UIViewController *qmui_rootViewController;
    
    /// QMUI 会修改 UINavigationController.interactivePopGestureRecognizer.delegate 的值,因此提供一个属性用于获取系统原始的值
    @property(nullable, nonatomic, weak, readonly) id<UIGestureRecognizerDelegate> qmui_interactivePopGestureRecognizerDelegate;
    
    - (void)qmui_pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^_Nullable)(void))completion;
    - (UIViewController *)qmui_popViewControllerAnimated:(BOOL)animated completion:(void (^_Nullable)(void))completion;
    - (NSArray<UIViewController *> *)qmui_popToViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^_Nullable)(void))completion;
    - (NSArray<UIViewController *> *)qmui_popToRootViewControllerAnimated:(BOOL)animated completion:(void (^_Nullable)(void))completion;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface CALayer (QMUI)
    
    /// 是否为某个 UIView 自带的 layer
    @property(nonatomic, assign, readonly) BOOL qmui_isRootLayerOfView;
    
    /// 暂停/恢复当前 layer 上的所有动画
    @property(nonatomic, assign) BOOL qmui_pause;
    
    /**
     *  设置四个角是否支持圆角的,iOS11 及以上会调用系统的接口,否则 QMUI 额外实现
     *  @warning 如果对应的 layer 有圆角,则请使用 QMUI_Border,否则系统的 border 会被 clip 掉
     *  @warning 使用 qmui 方法,则超出 layer 范围内的内容都会被 clip 掉,系统的则不会
     *  @warning 如果使用这个接口设置圆角,那么需要获取圆角的值需要用 qmui_originCornerRadius,否则 iOS 11 以下获取到的都是 0
     */
    @property(nonatomic, assign) QMUICornerMask qmui_maskedCorners;
    
    /// iOS11 以下 layer 自身的 cornerRadius 一直都是 0,圆角的是通过 mask 做的,qmui_originCornerRadius 保存了当前的圆角
    @property(nonatomic, assign, readonly) CGFloat qmui_originCornerRadius;
    
    /**
     *  把某个 sublayer 移动到当前所有 sublayers 的最后面
     *  @param sublayer 要被移动的 layer
     *  @warning 要被移动的 sublayer 必须已经添加到当前 layer 上
     */
    - (void)qmui_sendSublayerToBack:(CALayer *)sublayer;
    
    /**
     *  把某个 sublayer 移动到当前所有 sublayers 的最前面
     *  @param sublayer 要被移动的layer
     *  @warning 要被移动的 sublayer 必须已经添加到当前 layer 上
     */
    - (void)qmui_bringSublayerToFront:(CALayer *)sublayer;
    
    /**
     * 移除 CALayer(包括 CAShapeLayer 和 CAGradientLayer)所有支持动画的属性的默认动画,方便需要一个不带动画的 layer 时使用。
     */
    - (void)qmui_removeDefaultAnimations;
    
    /**
     * 对 CALayer 执行一些操作,不以动画的形式展示过程(默认情况下修改 CALayer 的属性都会以动画形式展示出来)。
     * @param actionsWithoutAnimation 要执行的操作,可以在里面修改 layer 的属性,例如 frame、backgroundColor 等。
     * @note 如果该 layer 的任何属性修改都不需要动画,也可使用 qmui_removeDefaultAnimations。
     */
    + (void)qmui_performWithoutAnimation:(void (NS_NOESCAPE ^)(void))actionsWithoutAnimation;
    
    /**
     * 生成虚线的方法,注意返回的是 CAShapeLayer
     * @param lineLength   每一段的线宽
     * @param lineSpacing  线之间的间隔
     * @param lineWidth    线的宽度
     * @param lineColor    线的颜色
     * @param isHorizontal 是否横向,因为画虚线的缘故,需要指定横向或纵向,横向是 YES,纵向是 NO。
     * 注意:暂不支持 dashPhase 和 dashPattens 数组设置,因为这些都定制性太强,如果用到则自己调用系统方法即可。
     */
    + (CAShapeLayer *)qmui_separatorDashLayerWithLineLength:(NSInteger)lineLength
                                                lineSpacing:(NSInteger)lineSpacing
                                                  lineWidth:(CGFloat)lineWidth
                                                  lineColor:(CGColorRef)lineColor
                                               isHorizontal:(BOOL)isHorizontal;
    
    /**
     
     * 产生一个通用分隔虚线的 layer,高度为 PixelOne,线宽为 2,线距为 2,默认会移除动画,并且背景色用 UIColorSeparator,注意返回的是 CAShapeLayer。
     
     * 其中,InHorizon 是横向;InVertical 是纵向。
     
     */
    + (CAShapeLayer *)qmui_separatorDashLayerInHorizontal;
    
    + (CAShapeLayer *)qmui_separatorDashLayerInVertical;
    
    /**
     * 产生一个适用于做通用分隔线的 layer,高度为 PixelOne,默认会移除动画,并且背景色用 UIColorSeparator
     */
    + (CALayer *)qmui_separatorLayer;
    
    /**
     * 产生一个适用于做列表分隔线的 layer,高度为 PixelOne,默认会移除动画,并且背景色用 TableViewSeparatorColor
     */
    + (CALayer *)qmui_separatorLayerForTableView;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface CALayer (QMUI_DynamicColor)
    
    /// 如果 layer 的 backgroundColor、borderColor、shadowColor 是使用 dynamic color(UIDynamicProviderColor、QMUIThemeColor 等)生成的,则调用这个方法可以重新设置一遍这些属性,从而更新颜色
    /// iOS 13 系统设置里的界面样式变化(Dark Mode),以及 QMUIThemeManager 触发的主题变化,都会自动调用 layer 的这个方法,业务无需关心。
    - (void)qmui_setNeedsUpdateDynamicStyle NS_REQUIRES_SUPER;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIControl (QMUI)
    
    /**
     *  是否接管 UIControl 的 touch 事件。
     *
     *  UIControl 在 UIScrollView 上会有300毫秒的延迟,默认情况下快速点击某个 UIControl,将不会看到 setHighlighted 的效果。如果通过将 UIScrollView.delaysContentTouches 置为 NO 来取消这个延迟,则系统无法判断 touch 时是要点击还是要滚动。
     *
     *  此时可以将 UIControl.qmui_automaticallyAdjustTouchHighlightedInScrollView 属性置为 YES,会使用自己的一套计算方式去判断触发 setHighlighted 的时机,从而保证既不影响 UIScrollView 的滚动,又能让 UIControl 在被快速点击时也能立马看到 setHighlighted 的效果。
     *
     *  @warning 使用了这个属性则不需要设置 UIScrollView.delaysContentTouches。
     */
    @property(nonatomic, assign) BOOL qmui_automaticallyAdjustTouchHighlightedInScrollView;
    
    /// 响应区域需要改变的大小,负值表示往外扩大,正值表示往内缩小
    @property(nonatomic,assign) UIEdgeInsets qmui_outsideEdge;
    
    /// setHighlighted: 方法的回调 block
    @property(nonatomic, copy) void (^qmui_setHighlightedBlock)(BOOL highlighted);
    
    /// 等同于 addTarget:action:forControlEvents:UIControlEventTouchUpInside
    @property(nonatomic, copy) void (^qmui_tapBlock)(__kindof UIControl *sender);
    
    @end
  • 这个分类提供额外的功能包括:

    1. 将给定的 UITableView 格式化为 QMUITableView 风格的样式,以统一为配置表里的值
    2. 计算给定的某个 view 处于哪个 indexPath 的 cell 上
    3. 计算给定的某个 view 处于哪个 sectionHeader 上
    4. 获取所有可视范围内的 sectionHeader 的 index
    5. 获取正处于 pinned 状态(也即悬停在顶部)的 sectionHeader 的 index
    6. 判断某个给定的 sectionHeader 是否处于 pinned 状态
    7. 判断某个给定的 cell indexPath 是否处于可视范围内
    8. 计算给定的 cell 的 indexPath 所对应的 QMUITableViewCellPosition
    9. 清除当前列表的所有 selection(选中的背景灰色)
    10. 判断列表当前内容是否足够滚动
    11. 让某个 row 滚动到指定的位置(系统默认只可以将 row 滚动到 Top/Middle/Bottom)
    12. 在将 searchBar 作为 tableHeaderView 的情况下,获取列表真实的 contentSize(系统为了实现列表内容不足一屏时依然可以将 searchBar 滚动到 navigationBar 下,在这种情况下会强制增大 contentSize)
    13. 在将 searchBar 作为 tableHeaderView 的情况下,判断列表内容是否足够多到可滚动
    See more

    Declaration

    Objective-C

    @interface UITableView (QMUI)
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIImage (QMUI)
    
    /**
     用于绘制一张图并以 UIImage 的形式返回
    
     @param size 要绘制的图片的 size,宽或高均不能为 0
     @param opaque 图片是否不透明,YES 表示不透明,NO 表示半透明
     @param scale 图片的倍数,0 表示取当前屏幕的倍数
     @param actionBlock 实际的图片绘制操作,在这里只管绘制就行,不用手动生成 image
     @return 返回绘制完的图片
     */
    + (nullable UIImage *)qmui_imageWithSize:(CGSize)size opaque:(BOOL)opaque scale:(CGFloat)scale actions:(void (^)(CGContextRef contextRef))actionBlock;
    
    /// 当前图片是否是可拉伸/平铺的,也即通过 resizableImageWithCapInsets: 处理过的图片
    @property(nonatomic, assign, readonly) BOOL qmui_resizable;
    
    /// 获取当前图片的像素大小,如果是多倍图,会被放大到一倍来算
    @property(nonatomic, assign, readonly) CGSize qmui_sizeInPixel;
    
    /**
     *  判断一张图是否不存在 alpha 通道,注意 “不存在 alpha 通道” 不等价于 “不透明”。一张不透明的图有可能是存在 alpha 通道但 alpha 值为 1。
     */
    - (BOOL)qmui_opaque;
    
    /**
     *  获取当前图片的均色,原理是将图片绘制到1px*1px的矩形内,再从当前区域取色,得到图片的均色。
     *  @link http://www.bobbygeorgescu.com/2011/08/finding-average-color-of-uiimage/ @/link
     *
     *  @return 代表图片平均颜色的UIColor对象
     */
    - (UIColor *)qmui_averageColor;
    
    /**
     *  置灰当前图片
     *
     *  @return 已经置灰的图片
     */
    - (nullable UIImage *)qmui_grayImage;
    
    /**
     *  设置一张图片的透明度
     *
     *  @param alpha 要用于渲染透明度
     *
     *  @return 设置了透明度之后的图片
     */
    - (nullable UIImage *)qmui_imageWithAlpha:(CGFloat)alpha;
    
    /**
     *  保持当前图片的形状不变,使用指定的颜色去重新渲染它,生成一张新图片并返回
     *
     *  @param tintColor 要用于渲染的新颜色
     *
     *  @return 与当前图片形状一致但颜色与参数tintColor相同的新图片
     */
    - (nullable UIImage *)qmui_imageWithTintColor:(nullable UIColor *)tintColor;
    
    /**
     *  以 CIColorBlendMode 的模式为当前图片叠加一个颜色,生成一张新图片并返回,在叠加过程中会保留图片内的纹理。
     *
     *  @param blendColor 要叠加的颜色
     *
     *  @return 基于当前图片纹理保持不变的情况下颜色变为指定的叠加颜色的新图片
     *
     *  @warning 这个方法可能比较慢,会卡住主线程,建议异步使用
     */
    - (nullable UIImage *)qmui_imageWithBlendColor:(nullable UIColor *)blendColor;
    
    /**
     *  在当前图片的基础上叠加一张图片,并指定绘制叠加图片的起始位置
     *
     *  叠加上去的图片将保持原图片的大小不变,不被压缩、拉伸
     *
     *  @param image 要叠加的图片
     *  @param point 所叠加图片的绘制的起始位置
     *
     *  @return 返回一张与原图大小一致的图片,所叠加的图片若超出原图大小,则超出部分被截掉
     */
    - (nullable UIImage *)qmui_imageWithImageAbove:(UIImage *)image atPoint:(CGPoint)point;
    
    /**
     *  在当前图片的上下左右增加一些空白(不支持负值),通常用于调节NSAttributedString里的图片与文字的间距
     *  @param extension 要拓展的大小
     *  @return 拓展后的图片
     */
    - (nullable UIImage *)qmui_imageWithSpacingExtensionInsets:(UIEdgeInsets)extension;
    
    /**
     *  切割出在指定位置中的图片
     *
     *  @param rect 要切割的rect
     *
     *  @return 切割后的新图片
     */
    - (nullable UIImage *)qmui_imageWithClippedRect:(CGRect)rect;
    
    
    /**
     *  切割出在指定圆角的图片
     *
     *  @param cornerRadius 要切割的圆角值
     *
     *  @return 切割后的新图片
     */
    - (nullable UIImage *)qmui_imageWithClippedCornerRadius:(CGFloat)cornerRadius;
    
    /**
     *  同上,可以设置 scale
     */
    
    - (nullable UIImage *)qmui_imageWithClippedCornerRadius:(CGFloat)cornerRadius scale:(CGFloat)scale;
    
    /**
     *  将原图以 QMUIImageResizingModeScaleAspectFit 的策略缩放,使其缩放后的大小不超过指定的大小,并返回缩放后的图片。缩放后的图片的倍数保持与原图一致。
     *  @param size 在这个约束的 size 内进行缩放后的大小,处理后返回的图片的 size 会根据 resizingMode 不同而不同,但必定不会超过 size。
     *
     *  @return 处理完的图片
     *  @see qmui_imageResizedInLimitedSize:resizingMode:scale:
     */
    - (nullable UIImage *)qmui_imageResizedInLimitedSize:(CGSize)size;
    
    /**
     *  将原图按指定的 QMUIImageResizingMode 缩放,使其缩放后的大小不超过指定的大小,并返回缩放后的图片,缩放后的图片的倍数保持与原图一致。
     *  @param size 在这个约束的 size 内进行缩放后的大小,处理后返回的图片的 size 会根据 resizingMode 不同而不同,但必定不会超过 size。
     *  @param resizingMode 希望使用的缩放模式
     *
     *  @return 处理完的图片
     *  @see qmui_imageResizedInLimitedSize:resizingMode:scale:
     */
    - (nullable UIImage *)qmui_imageResizedInLimitedSize:(CGSize)size resizingMode:(QMUIImageResizingMode)resizingMode;
    
    /**
     *  将原图按指定的 QMUIImageResizingMode 缩放,使其缩放后的大小不超过指定的大小,并返回缩放后的图片。
     *  @param size 在这个约束的 size 内进行缩放后的大小,处理后返回的图片的 size 会根据 resizingMode 不同而不同,但必定不会超过 size。
     *  @param resizingMode 希望使用的缩放模式
     *  @param scale 用于指定缩放后的图片的倍数
     *
     *  @return 处理完的图片
     */
    - (nullable UIImage *)qmui_imageResizedInLimitedSize:(CGSize)size resizingMode:(QMUIImageResizingMode)resizingMode scale:(CGFloat)scale;
    
    /**
     *  将原图进行旋转,只能选择上下左右四个方向
     *
     *  @param  direction 旋转的方向
     *
     *  @return 处理完的图片
     */
    - (nullable UIImage *)qmui_imageWithOrientation:(UIImageOrientation)direction;
    
    /**
     *  为图片加上一个border,border的路径为path
     *
     *  @param borderColor  border的颜色
     *  @param path         border的路径
     *
     *  @return 带border的UIImage
     *  @warning 注意通过`path.lineWidth`设置边框大小,同时注意路径要考虑像素对齐(`path.lineWidth / 2.0`)
     */
    - (nullable UIImage *)qmui_imageWithBorderColor:(nullable UIColor *)borderColor path:(nullable UIBezierPath *)path;
    
    /**
     *  为图片加上一个border,border的路径为borderColor、cornerRadius和borderWidth所创建的path
     *
     *  @param borderColor   border的颜色
     *  @param borderWidth    border的宽度
     *  @param cornerRadius  border的圆角
     *
     *  @param dashedLengths 一个CGFloat的数组,例如`CGFloat dashedLengths[] = {2, 4}`。如果不需要虚线,则传0即可
     *
     *  @return 带border的UIImage
     */
    - (nullable UIImage *)qmui_imageWithBorderColor:(nullable UIColor *)borderColor borderWidth:(CGFloat)borderWidth cornerRadius:(CGFloat)cornerRadius dashedLengths:(nullable const CGFloat *)dashedLengths;
    - (nullable UIImage *)qmui_imageWithBorderColor:(nullable UIColor *)borderColor borderWidth:(CGFloat)borderWidth cornerRadius:(CGFloat)cornerRadius;
    
    
    /**
     *  为图片加上一个border(可以是任意一条边,也可以是多条组合;只能创建矩形的border,不能添加圆角)
     *
     *  @param borderColor       border的颜色
     *  @param borderWidth        border的宽度
     *  @param borderPosition    border的位置
     *
     *  @return 带border的UIImage
     */
    - (nullable UIImage *)qmui_imageWithBorderColor:(nullable UIColor *)borderColor borderWidth:(CGFloat)borderWidth borderPosition:(QMUIImageBorderPosition)borderPosition;
    
    /**
     *  返回一个被mask的图片
     *
     *  @param maskImage             mask图片
     *  @param usingMaskImageMode    是否使用“mask image”的方式,若为 YES,则黑色部分显示,白色部分消失,透明部分显示,其他颜色会按照颜色的灰色度对图片做透明处理。若为 NO,则 maskImage 要求必须为灰度颜色空间的图片(黑白图),白色部分显示,黑色部分消失,透明部分消失,其他灰色度对图片做透明处理。
     *
     *  @return 被mask的图片
     */
    - (nullable UIImage *)qmui_imageWithMaskImage:(UIImage *)maskImage usingMaskImageMode:(BOOL)usingMaskImageMode;
    
    /**
     将 data 转换成 animated UIImage(如果非 animated 则转换成普通 UIImage),image 倍数为 1(与系统的 [UIImage imageWithData:] 接口一致)
    
     @param data 图片文件的 data
     @return 转换成的 UIImage
     */
    + (nullable UIImage *)qmui_animatedImageWithData:(NSData *)data;
    
    /**
     将 data 转换成 animated UIImage(如果非 animated 则转换成普通 UIImage)
    
     @param data 图片文件的 data
     @param scale 图片的倍数,0 表示获取当前设备的屏幕倍数
     @return 转换成的 UIImage
     @see http://www.jianshu.com/p/767af9c690a3
     @see https://github.com/rs/SDWebImage
     */
    + (nullable UIImage *)qmui_animatedImageWithData:(NSData *)data scale:(CGFloat)scale;
    
    /**
     在 mainBundle 里找到对应名字的图片, 注意图片 scale 为 1,与系统的 [UIImage imageWithData:] 接口一致,若需要修改倍数,请使用 -qmui_animatedImageNamed:scale:
    
     @param name 图片名,可指定后缀,若不写后缀,默认为“gif”。不写后缀的情况下会先找“gif”后缀的图片,不存在再找无后缀的文件,仍不存在则返回 nil
     @return  转换成的 UIImage
     */
    + (nullable UIImage *)qmui_animatedImageNamed:(NSString *)name;
    
    /**
     在 mainBundle 里找到对应名字的图片
     
     @param name 图片名,可指定后缀,若不写后缀,默认为“gif”。不写后缀的情况下会先找“gif”后缀的图片,不存在再找无后缀的文件,仍不存在则返回 nil
     @param scale 图片的倍数,0 表示获取当前设备的屏幕倍数
     @return  转换成的 UIImage
     */
    + (nullable UIImage *)qmui_animatedImageNamed:(NSString *)name scale:(CGFloat)scale;
    
    /**
     *  创建一个size为(4, 4)的纯色的UIImage
     *
     *  @param color 图片的颜色
     *
     *  @return 纯色的UIImage
     */
    + (nullable UIImage *)qmui_imageWithColor:(nullable UIColor *)color;
    
    /**
     *  创建一个纯色的UIImage
     *
     *  @param  color           图片的颜色
     *  @param  size            图片的大小
     *  @param  cornerRadius    图片的圆角
     *
     * @return 纯色的UIImage
     */
    + (nullable UIImage *)qmui_imageWithColor:(nullable UIColor *)color size:(CGSize)size cornerRadius:(CGFloat)cornerRadius;
    
    /**
     *  创建一个纯色的UIImage,支持为四个角设置不同的圆角
     *  @param  color               图片的颜色
     *  @param  size                图片的大小
     *  @param  cornerRadius   四个角的圆角值的数组,长度必须为4,顺序分别为[左上角、左下角、右下角、右上角]
     */
    + (nullable UIImage *)qmui_imageWithColor:(nullable UIColor *)color size:(CGSize)size cornerRadiusArray:(nullable NSArray<NSNumber *> *)cornerRadius;
    
    /**
     *  创建一个带边框路径,没有背景色的路径图片,border的路径为path
     *
     *  @param strokeColor  border的颜色
     *  @param path         border的路径
     *  @param addClip      是否要调path的addClip
     *
     *  @return 带border的UIImage
     */
    + (nullable UIImage *)qmui_imageWithStrokeColor:(nullable UIColor *)strokeColor size:(CGSize)size path:(nullable UIBezierPath *)path addClip:(BOOL)addClip;
    
    /**
     *  创建一个带边框路径,没有背景色的路径图片,border的路径为strokeColor、cornerRadius和lineWidth所创建的path
     *
     *  @param strokeColor  border的颜色
     *  @param lineWidth    border的宽度
     *  @param cornerRadius border的圆角
     *
     *  @return 带border的UIImage
     */
    + (nullable UIImage *)qmui_imageWithStrokeColor:(nullable UIColor *)strokeColor size:(CGSize)size lineWidth:(CGFloat)lineWidth cornerRadius:(CGFloat)cornerRadius;
    
    /**
     *  创建一个带边框路径,没有背景色的路径图片(可以是任意一条边,也可以是多条组合;只能创建矩形的border,不能添加圆角)
     *
     *  @param strokeColor        路径的颜色
     *  @param size               图片的大小
     *  @param lineWidth          路径的大小
     *  @param borderPosition     图片的路径位置,上左下右
     *
     *  @return 带路径,没有背景色的UIImage
     */
    + (nullable UIImage *)qmui_imageWithStrokeColor:(nullable UIColor *)strokeColor size:(CGSize)size lineWidth:(CGFloat)lineWidth borderPosition:(QMUIImageBorderPosition)borderPosition;
    /**
     *  创建一个指定大小和颜色的形状图片
     *  @param shape 图片形状
     *  @param size 图片大小
     *  @param tintColor 图片颜色
     */
    + (nullable UIImage *)qmui_imageWithShape:(QMUIImageShape)shape size:(CGSize)size tintColor:(nullable UIColor *)tintColor;
    
    /**
     *  创建一个指定大小和颜色的形状图片
     *  @param shape 图片形状
     *  @param size 图片大小
     *  @param lineWidth 路径大小,不会影响最终size
     *  @param tintColor 图片颜色
     */
    + (nullable UIImage *)qmui_imageWithShape:(QMUIImageShape)shape size:(CGSize)size lineWidth:(CGFloat)lineWidth tintColor:(nullable UIColor *)tintColor;
    
    /**
     *  将文字渲染成图片,最终图片和文字一样大
     */
    + (nullable UIImage *)qmui_imageWithAttributedString:(NSAttributedString *)attributedString;
    
    /**
     对传进来的 `UIView` 截图,生成一个 `UIImage` 并返回。注意这里使用的是 view.layer 来渲染图片内容。
    
     @param view 要截图的 `UIView`
    
     @return `UIView` 的截图
     
     @warning UIView 的 transform 并不会在截图里生效
     */
    + (nullable UIImage *)qmui_imageWithView:(UIView *)view;
    
    /**
     对传进来的 `UIView` 截图,生成一个 `UIImage` 并返回。注意这里使用的是 iOS 7的系统截图接口。
    
     @param view         要截图的 `UIView`
     @param afterUpdates 是否要在界面更新完成后才截图
    
     @return `UIView` 的截图
     
     @warning UIView 的 transform 并不会在截图里生效
     */
    + (nullable UIImage *)qmui_imageWithView:(UIView *)view afterScreenUpdates:(BOOL)afterUpdates;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UINavigationBar (QMUI)
    
    /**
     UINavigationBar 在 iOS 11 下所有的 item 都会由 contentView 管理,只要在 UINavigationController init 完成后就能拿到 qmui_contentView 的值
     */
    @property(nonatomic, strong, readonly, nullable) UIView *qmui_contentView API_AVAILABLE(ios(11.0));
    
    /**
     UINavigationBar 的背景 view,可能显示磨砂、背景图,顶部有一部分溢出到 UINavigationBar 外。
     
     在 iOS 10 及以后是私有的 _UIBarBackground 类。
     
     在 iOS 9 及以前是私有的 _UINavigationBarBackground 类。
     */
    @property(nonatomic, strong, readonly, nullable) UIView *qmui_backgroundView;
    
    /**
     qmui_backgroundView 内显示实际背景的 view,可能是磨砂或者背景图片。
     
     在 iOS 10 及以后,该 view 为 qmui_backgroundView 的 subview,当显示磨砂时是一个 UIVisualEffectView,当显示背景图时是一个 UIImageView。
     
     在 iOS 9 及以前,如果显示磨砂,该 view 为 qmui_backgroundView 的 subview,是一个 _UIBackdropView,如果显示背景图,则返回 qmui_backgroundView 自身,因为 _UINavigationBarBackground 本身就是一个 UIImageView。
     
     @warning 如果要以 view 的方式去修改 UINavigationBar 的背景,由于不同的 iOS 版本,qmui_shadowImageView 和 qmui_backgroundContentView 的层级关系不同,所以为了效果的统一,建议这种情况下操作 qmui_backgroundView 会好过于操作 qmui_backgroundContentView。
     */
    @property(nonatomic, strong, readonly, nullable) __kindof UIView *qmui_backgroundContentView;
    
    /**
     qmui_backgroundView 内的 subview,用于显示底部分隔线 shadowImage,注意这个 view 是溢出到 qmui_backgroundView 外的。若 shadowImage 为 [UIImage new],则这个 view 的高度为 0。
     */
    @property(nonatomic, strong, readonly, nullable) UIImageView *qmui_shadowImageView;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIColor (QMUI)
    
    /**
     *  使用HEX命名方式的颜色字符串生成一个UIColor对象
     *
     *  @param hexString 支持以 # 开头和不以 # 开头的 hex 字符串
     *      #RGB        例如#f0f,等同于#ffff00ff,RGBA(255, 0, 255, 1)
     *      #ARGB       例如#0f0f,等同于#00ff00ff,RGBA(255, 0, 255, 0)
     *      #RRGGBB     例如#ff00ff,等同于#ffff00ff,RGBA(255, 0, 255, 1)
     *      #AARRGGBB   例如#00ff00ff,等同于RGBA(255, 0, 255, 0)
     *
     * @return UIColor对象
     */
    + (nullable UIColor *)qmui_colorWithHexString:(nullable NSString *)hexString;
    
    /**
     *  将当前色值转换为hex字符串,通道排序是AARRGGBB(与Android保持一致)
     *  @return 色值对应的 hex 字符串,以 # 开头,例如 #00ff00ff
     */
    - (NSString *)qmui_hexString;
    
    /**
     *  获取当前 UIColor 对象里的红色色值
     *
     *  @return 红色通道的色值,值范围为0.0-1.0
     */
    - (CGFloat)qmui_red;
    
    /**
     *  获取当前 UIColor 对象里的绿色色值
     *
     *  @return 绿色通道的色值,值范围为0.0-1.0
     */
    - (CGFloat)qmui_green;
    
    /**
     *  获取当前 UIColor 对象里的蓝色色值
     *
     *  @return 蓝色通道的色值,值范围为0.0-1.0
     */
    - (CGFloat)qmui_blue;
    
    /**
     *  获取当前 UIColor 对象里的透明色值
     *
     *  @return 透明通道的色值,值范围为0.0-1.0
     */
    - (CGFloat)qmui_alpha;
    
    /**
     *  获取当前 UIColor 对象里的 hue(色相),注意 hue 的值是一个角度,所以0和1(0°和360°)是等价的,用 return 值去做判断时要特别注意。
     */
    - (CGFloat)qmui_hue;
    
    /**
     *  获取当前 UIColor 对象里的 saturation(饱和度)
     */
    - (CGFloat)qmui_saturation;
    
    /**
     *  获取当前 UIColor 对象里的 brightness(亮度)
     */
    - (CGFloat)qmui_brightness;
    
    /**
     *  将当前UIColor对象剥离掉alpha通道后得到的色值。相当于把当前颜色的半透明值强制设为1.0后返回
     *
     *  @return alpha通道为1.0,其他rgb通道与原UIColor对象一致的新UIColor对象
     */
    - (nullable UIColor *)qmui_colorWithoutAlpha;
    
    /**
     *  计算当前color叠加了alpha之后放在指定颜色的背景上的色值
     */
    - (UIColor *)qmui_colorWithAlpha:(CGFloat)alpha backgroundColor:(nullable UIColor *)backgroundColor;
    
    /**
     *  计算当前color叠加了alpha之后放在白色背景上的色值
     */
    - (UIColor *)qmui_colorWithAlphaAddedToWhite:(CGFloat)alpha;
    
    /**
     *  将自身变化到某个目标颜色,可通过参数progress控制变化的程度,最终得到一个纯色
     *  @param toColor 目标颜色
     *  @param progress 变化程度,取值范围0.0f~1.0f
     */
    - (UIColor *)qmui_transitionToColor:(nullable UIColor *)toColor progress:(CGFloat)progress;
    
    /**
     *  判断当前颜色是否为深色,可用于根据不同色调动态设置不同文字颜色的场景。
     *
     *  @link http://stackoverflow.com/questions/19456288/text-color-based-on-background-image @/link
     *
     *  @return 若为深色则返回“YES”,浅色则返回“NO”
     */
    - (BOOL)qmui_colorIsDark;
    
    /**
     *  @return 当前颜色的反色,不管传入的颜色属于什么 colorSpace,最终返回的反色都是 RGB
     *
     *  @link http://stackoverflow.com/questions/5893261/how-to-get-inverse-color-from-uicolor @/link
     */
    - (UIColor *)qmui_inverseColor;
    
    /**
     *  判断当前颜色是否等于系统默认的 tintColor 颜色。
     *  背景:如果将一个 UIView.tintColor 设置为 nil,表示这个 view 的 tintColor 希望跟随 superview.tintColor 变化而变化,所以设置完再获取 view.tintColor,得到的并非 nil,而是 superview.tintColor 的值,而如果整棵 view 层级树里的 view 都没有设置自己的 tintColor,则会返回系统默认的 tintColor(也即 [UIColor qmui_systemTintColor]),所以才提供这个方法用于代替判断 tintColor == nil 的作用。
     */
    - (BOOL)qmui_isSystemTintColor;
    
    /**
     *  获取当前系统的默认 tintColor 色值
     */
    + (UIColor *)qmui_systemTintColor;
    
    /**
     *  计算两个颜色叠加之后的最终色(注意区分前景色后景色的顺序)<br/>
     *  @link http://stackoverflow.com/questions/10781953/determine-rgba-colour-received-by-combining-two-colours @/link
     */
    + (UIColor *)qmui_colorWithBackendColor:(UIColor *)backendColor frontColor:(UIColor *)frontColor;
    
    /**
     *  将颜色A变化到颜色B,可通过progress控制变化的程度
     *  @param fromColor 起始颜色
     *  @param toColor 目标颜色
     *  @param progress 变化程度,取值范围0.0f~1.0f
     */
    + (UIColor *)qmui_colorFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor progress:(CGFloat)progress;
    
    /**
     *  产生一个随机色,大部分情况下用于测试
     */
    + (UIColor *)qmui_randomColor;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSNumber (QMUI)
    
    @property(nonatomic, assign, readonly) CGFloat qmui_CGFloatValue;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIScrollView (QMUI)
    
    /// 判断UIScrollView是否已经处于顶部(当UIScrollView内容不够多不可滚动时,也认为是在顶部)
    @property(nonatomic, assign, readonly) BOOL qmui_alreadyAtTop;
    
    /// 判断UIScrollView是否已经处于底部(当UIScrollView内容不够多不可滚动时,也认为是在底部)
    @property(nonatomic, assign, readonly) BOOL qmui_alreadyAtBottom;
    
    /// UIScrollView 的真正 inset,在 iOS11 以后需要用到 adjustedContentInset 而在 iOS11 以前只需要用 contentInset
    @property(nonatomic, assign, readonly) UIEdgeInsets qmui_contentInset;
    
    /**
     UIScrollView 默认的 contentInset,会自动将 contentInset 和 scrollIndicatorInsets 都设置为这个值并且调用一次 qmui_scrollToTopUponContentInsetTopChange 设置默认的 contentOffset,一般用于 UIScrollViewContentInsetAdjustmentNever 的列表。
     @warning 如果 scrollView 被添加到某个 viewController 上,则只有在 viewController viewDidAppear 之前(不包含 viewDidAppear)设置这个属性才会自动滚到顶部,如果在 viewDidAppear 之后才添加到 viewController 上,则只有第一次设置 qmui_initialContentInset 时才会滚动到顶部。这样做的目的是为了避免在 scrollView 已经显示出来并滚动到列表中间后,由于某些原因,contentInset 发生了中间值的变动(也即一开始是正确的值,中间变成错误的值,再变回正确的值),此时列表会突然跳到顶部的问题。
     */
    @property(nonatomic, assign) UIEdgeInsets qmui_initialContentInset;
    
    /**
     * 判断当前的scrollView内容是否足够滚动
     * @warning 避免与<i>scrollEnabled</i>混淆
     */
    - (BOOL)qmui_canScroll;
    
    /**
     * 不管当前scrollView是否可滚动,直接将其滚动到最顶部
     * @param force 是否无视[self qmui_canScroll]而强制滚动
     * @param animated 是否用动画表现
     */
    - (void)qmui_scrollToTopForce:(BOOL)force animated:(BOOL)animated;
    
    /**
     * 等同于[self qmui_scrollToTopForce:NO animated:animated]
     */
    - (void)qmui_scrollToTopAnimated:(BOOL)animated;
    
    /// 等同于[self qmui_scrollToTopAnimated:NO]
    - (void)qmui_scrollToTop;
    
    /**
     滚到列表顶部,但如果 contentInset.top 与上一次相同则不会执行滚动操作,通常用于 UIScrollViewContentInsetAdjustmentNever 的 scrollView 设置完业务的 contentInset 后将列表滚到顶部。
     */
    - (void)qmui_scrollToTopUponContentInsetTopChange;
    
    /**
     * 如果当前的scrollView可滚动,则将其滚动到最底部
     * @param animated 是否用动画表现
     * @see [UIScrollView qmui_canScroll]
     */
    - (void)qmui_scrollToBottomAnimated:(BOOL)animated;
    
    /// 等同于[self qmui_scrollToBottomAnimated:NO]
    - (void)qmui_scrollToBottom;
    
    // 立即停止滚动,用于那种手指已经离开屏幕但列表还在滚动的情况。
    - (void)qmui_stopDeceleratingIfNeeded;
    
    /**
     以动画的形式修改 contentInset
    
     @param contentInset 要修改为的 contentInset
     @param animated 是否要使用动画修改
     */
    - (void)qmui_setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface NSArray<ObjectType> (QMUI)
    
    /**
     将多个对象合并成一个数组,如果参数类型是数组则会将数组内的元素拆解出来加到 return 内(只会拆解一层,所以多维数组不处理)
    
     @param object 要合并的多个数组
     @return 合并完的结果
     */
    + (instancetype)qmui_arrayWithObjects:(ObjectType)object, ...;
    
    /**
     *  将多维数组打平成一维数组再遍历所有子元素
     */
    - (void)qmui_enumerateNestedArrayWithBlock:(void (NS_NOESCAPE^)(id obj, BOOL *stop))block;
    
    /**
     *  将多维数组递归转换成 mutable 多维数组
     */
    - (NSMutableArray *)qmui_mutableCopyNestedArray;
    
    /**
     *  过滤数组元素,将 block 返回 YES 的 item 重新组装成一个数组返回
     */
    - (NSArray<ObjectType> *)qmui_filterWithBlock:(BOOL (NS_NOESCAPE^)(ObjectType item))block;
    
    /**
    *  转换数组元素,将每个 item 都经过 block 转换成一遍 返回转换后的新数组
    */
    - (NSArray *)qmui_mapWithBlock:(id (NS_NOESCAPE^)(ObjectType item))block;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UITextField (QMUI)
    
    /// UITextField只有selectedTextRange属性(在<UITextInput>协议里定义),这里拓展了一个方法可以将UITextRange类型的selectedTextRange转换为NSRange类型的selectedRange
    @property(nonatomic, assign, readonly) NSRange qmui_selectedRange;
    
    /// 输入框右边的 clearButton,在 UITextField 初始化后就存在
    @property(nullable, nonatomic, weak, readonly) UIButton *qmui_clearButton;
    
    /// 自定义 clearButton 的图片,设置成nil,恢复到系统默认的图片
    @property(nullable, nonatomic, strong) UIImage *qmui_clearButtonImage UI_APPEARANCE_SELECTOR;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIButton (QMUI)
    
    typedef NS_ENUM(NSUInteger, QMUICustomizeButtonPropType) {
        QMUICustomizeButtonPropTypeTitle,
        QMUICustomizeButtonPropTypeTitleColor,
        QMUICustomizeButtonPropTypeTitleShadowColor,
        QMUICustomizeButtonPropTypeImage,
        QMUICustomizeButtonPropTypeBackgroundImage,
        QMUICustomizeButtonPropTypeAttributedTitle
    };
    
    - (instancetype)qmui_initWithImage:(UIImage *)image title:(NSString *)title;
    
    /**
     * 判断该 button 在特定 UIControlState 下是否设置了属性
     * @note 该方法会对设置了任何 QMUICustomizeButtonPropType 都返回 YES
     */
    - (BOOL)qmui_hasCustomizedButtonPropForState:(UIControlState)state;
    
    /**
     * 判断该 button 在特定 UIControlState 下是否设置了某个 QMUICustomizeButtonPropType 属性
     * @param type 对应于 UIbutton 的 setXXX:forState 办法
     */
    - (BOOL)qmui_hasCustomizedButtonPropWithType:(QMUICustomizeButtonPropType)type forState:(UIControlState)state;
    
    /**
     * 在UIButton的样式(如字体)设置完后,将button的text设置为一个测试字符,再调用sizeToFit,从而令button的高度适应字体
     * @warning 会调用<i>setText:forState:</i>,因此请确保在设置完按钮的样式之后、设置text之前调用
     */
    - (void)qmui_calculateHeightAfterSetAppearance;
    
    /**
     * 通过这个方法设置了 attributes 之后,setTitle:forState: 会自动把文字转成 attributedString 再添加上去,无需每次都自己构造 attributedString
     * @note 即使先调用 setTitle:forState: 然后再调用这个方法,之前的 title 仍然会被应用上这些 attributes
     * @note 该方法和 setTitleColor:forState: 均可设置字体颜色,如果二者冲突,则代码顺序较后的方法定义的颜色会最终生效
     * @note 如果包含了 NSKernAttributeName ,则此方法会自动帮你去掉最后一个字的 kern 效果,否则容易导致文字整体在视觉上不居中
     */
    - (void)qmui_setTitleAttributes:(NSDictionary<NSAttributedStringKey, id> *)attributes forState:(UIControlState)state;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UILabel (QMUI)
    
    - (instancetype)qmui_initWithFont:(nullable UIFont *)font textColor:(nullable UIColor *)textColor;
    
    /**
     * @brief 在需要特殊样式时,可通过此属性直接给整个 label 添加 NSAttributeName 系列样式,然后 setText 即可,无需使用繁琐的 attributedText
     *
     * @note 即使先调用 setText/attributedText ,然后再设置此属性,此属性仍然会生效
     * @note 如果此属性包含了 NSKernAttributeName ,则最后一个字的 kern 效果会自动被移除,否则容易导致文字在视觉上不居中
     *
     * @note 当你设置了此属性后,每次你调用 setText: 时,其实都会被自动转而调用 setAttributedText:
     *
     * 现在你有三种方法控制 label 的样式:
     * 1. 本身的样式属性(如 textColor, font 等)
     * 2. qmui_textAttributes
     * 3. 构造 NSAttributedString
     * 这三种方式可以同时使用,如果样式发生冲突(比如先通过方法1将文字设成红色,又通过方法2将文字设成蓝色),则绝大部分情况下代码执行顺序靠后的会最终生效
     * 唯一例外的极端情况是:先用方法2将文字设成红色,再用方法1将文字设成蓝色,最后再 setText,这时虽然代码执行顺序靠后的是方法1,但最终生效的会是方法2,为了避免这种极端情况的困扰,建议不要同时使用方法1和方法2去设置同一种样式。
     *
     */
    @property(nullable, nonatomic, copy) NSDictionary<NSAttributedStringKey, id> *qmui_textAttributes;
    
    /** 
     *  Setter 设置当前整段文字的行高
     *  @note 如果同时通过 qmui_textAttributes 或 attributedText 给整段文字设置了行高,则此方法将不再生效。换句话说,此方法设置的行高将永远不会覆盖 qmui_textAttributes 或 attributedText 设置的行高。
     *  @note 比如对于字符串"abc",你通过 attributedText 设置 {0, 1} 这个 range 范围内的行高为 10,又通过 setQmui_lineHeight: 设置了整体行高为 20,则最终 {0, 1} 内的行高将为 10,而 {1, 2} 内的行高将为全局行高 20
     *  @note 比如对于字符串"abc",你先通过 setQmui_lineHeight: 设置整体行高为 10,又通过 attributedText/qmui_textAttributes 设置整体行高为 20,无论这两个设置的代码的先后顺序如何,最终行高都将为 20
     *  @note 你可以通过设置 'QMUILineHeightIdentity' 来恢复 UILabel 默认的行高
     *  @note 当你设置了此属性后,每次你调用 setText: 时,其实都会被自动转而调用 setAttributedText:
     *
     *  -----------------------------------
     *
     *  Getter 获取整段文字的行高
     *  @note 如果通过 setQmui_lineHeight 设置行高,会优先返回该值。
     *  @note 如果通过 NSParagraphStyleAttributeName 设置了行高,同时 range 是整段文字,则会返回 paraStyle.maximumLineHeight。
     *  @note 如果通过 setText 设置文本,会返回 font.lineHeight。
     *  @warning 除上述情况外,计算的数值都可能不准确,会返回 0。
     *
     */
    
    @property(nonatomic, assign) CGFloat qmui_lineHeight;
    
    /**
     * 将目标UILabel的样式属性设置到当前UILabel上
     *
     * 将会复制的样式属性包括:font、textColor、backgroundColor
     * @param label 要从哪个目标UILabel上复制样式
     */
    - (void)qmui_setTheSameAppearanceAsLabel:(UILabel *)label;
    
    /**
     * 在UILabel的样式(如字体)设置完后,将label的text设置为一个测试字符,再调用sizeToFit,从而令label的高度适应字体
     * @warning 会setText:,因此确保在配置完样式后、设置text之前调用
     */
    - (void)qmui_calculateHeightAfterSetAppearance;
    
    /**
     * UILabel在显示中文字符时,会比显示纯英文字符额外多了一个sublayers,并且这个layer超出了label.bounds的范围,这会导致label必定需要做像素合成,所以通过一些方式来避免合成操作
     * @see http://stackoverflow.com/questions/34895641/uilabel-is-marked-as-red-when-color-blended-layers-is-selected
     */
    - (void)qmui_avoidBlendedLayersIfShowingChineseWithBackgroundColor:(UIColor *)color;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UICollectionView (QMUI)
    
    /**
     *  清除所有已选中的item的选中状态
     */
    - (void)qmui_clearsSelection;
    
    /**
     *  重新`reloadData`,同时保持`reloadData`前item的选中状态
     */
    - (void)qmui_reloadDataKeepingSelection;
    
    /**
     *  获取某个view在collectionView内对应的indexPath
     *
     *  例如某个view是某个cell里的subview,在这个view的点击事件回调方法里,就能通过`qmui_indexPathForItemAtView:`获取被点击的view所处的cell的indexPath
     *
     *  @warning 注意返回的indexPath有可能为nil,要做保护。
     */
    - (NSIndexPath *)qmui_indexPathForItemAtView:(id)sender;
    
    /**
     *  判断当前 indexPath 的 item 是否为可视的 item
     */
    - (BOOL)qmui_itemVisibleAtIndexPath:(NSIndexPath *)indexPath;
    
    /**
     *  对系统的 indexPathsForVisibleItems 进行了排序后的结果
     */
    - (NSArray<NSIndexPath *> *)qmui_indexPathsForVisibleItems;
    
    /**
     *  获取可视区域内第一个cell的indexPath。
     *
     *  为什么需要这个方法是因为系统的indexPathsForVisibleItems方法返回的数组成员是无序排列的,所以不能直接通过firstObject拿到第一个cell。
     *
     *  @warning 若可视区域为CGRectZero,则返回nil
     */
    - (NSIndexPath *)qmui_indexPathForFirstVisibleCell;
    
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIGestureRecognizer (QMUI)
    
    /// 获取当前手势直接作用到的 view(注意与 view 属性区分开:view 属性表示手势被添加到哪个 view 上,qmui_targetView 则是 view 属性里的某个 subview)
    @property(nullable, nonatomic, weak, readonly) UIView *qmui_targetView;
    @end
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface UIImageView (QMUI)
    
    /**
     暂停/恢复当前 UIImageView 上的 animation images(包括通过 animationImages 设置的图片数组,以及通过 [UIImage animatedImage] 系列方法创建的动图)的播放,默认为 NO。
     */
    @property(nonatomic, assign) BOOL qmui_pause;
    
    /**
     是否要用 QMUI 提供的高性能方式去渲染由 [UIImage animatedImage] 创建的 UIImage,(系统原生的方式在 UIImageView 被放在 UIScrollView 内时会卡顿),默认为 YES。
     */
    @property(nonatomic, assign) BOOL qmui_smoothAnimation;
    
    /**
     *  把 UIImageView 的宽高调整为能保持 image 宽高比例不变的同时又不超过给定的 `limitSize` 大小的最大frame
     *
     *  建议在设置完x/y之后调用
     */
    - (void)qmui_sizeToFitKeepingImageAspectRatioInSize:(CGSize)limitSize;
    @end