QMUIPopupContainerView

@interface QMUIPopupContainerView : UIControl {
  CAShapeLayer *_backgroundLayer;
  CGFloat _arrowMinX;
  CGFloat _arrowMinY;
}

带箭头的小tips浮层,自带 imageView 和 textLabel,可展示简单的图文信息,支持 UIViewContentModeTop/UIViewContentModeBottom/UIViewContentModeCenter 三种布局方式。 QMUIPopupContainerView 支持以两种方式显示在界面上:

  1. 添加到某个 UIView 上(适合于 viewController 切换时浮层跟着一起切换的场景),这种场景只能手动隐藏浮层。
  2. 在 QMUIPopupContainerView 自带的 UIWindow 里显示(适合于用完就消失的场景,不要涉及界面切换),这种场景支持点击空白地方自动隐藏浮层。

使用步骤:

  1. 调用 init 方法初始化。
  2. 选择一种显示方式: 2.1 如果要添加到某个 UIView 上,则先设置浮层 hidden = YES,然后调用 addSubview: 把浮层添加到目标 UIView 上。 2.2 如果是轻量的场景用完即走,则 init 完浮层即可,无需设置 hidden,也无需调用 addSubview:,在后面第 4 步里会自动把浮层添加到 UIWindow 上显示出来。
  3. 通过为 sourceBarItem/sourceView/sourceRect 三者中的一个赋值,来决定浮层布局的位置。
  4. 调用 showWithAnimated: 或 showWithAnimated:completion: 显示浮层。
  5. 调用 hideWithAnimated: 或 hideWithAnimated:completion: 隐藏浮层。

Warning

如果使用方法 2.2,并且没有打开 automaticallyHidesWhenUserTap 属性,则记得在适当的时机(例如 viewWillDisappear:)隐藏浮层。

如果默认功能无法满足需求,可继承它重写一个子类,继承要点:

  1. 初始化时要做的事情请放在 didInitialize 里。
  2. 所有 subviews 请加到 contentView 上。
  3. 通过重写 sizeThatFitsInContentView:,在里面返回当前 subviews 的大小。
  4. 在 layoutSubviews: 里,所有 subviews 请相对于 contentView 布局。
  • Undocumented

    Declaration

    Objective-C

    CAShapeLayer    *_backgroundLayer
  • Undocumented

    Declaration

    Objective-C

    CGFloat         _arrowMinX
  • Undocumented

    Declaration

    Objective-C

    CGFloat         _arrowMinY
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, assign) BOOL debug

    Swift

    var debug: Bool { get set }
  • 在浮层显示时,点击空白地方是否要自动隐藏浮层,仅在用方法 2 显示时有效。 默认为 NO,也即需要手动调用代码去隐藏浮层。

    Declaration

    Objective-C

    @property (nonatomic) BOOL automaticallyHidesWhenUserTap;

    Swift

    var automaticallyHidesWhenUserTap: Bool { get set }
  • 所有subview都应该添加到contentView上,默认contentView.userInteractionEnabled = NO,需要事件操作时自行打开

    Declaration

    Objective-C

    @property (nonatomic, strong, readonly) UIView *contentView;

    Swift

    var contentView: UIView! { get }
  • 预提供的UIImageView,默认为nil,调用到的时候才初始化

    Declaration

    Objective-C

    @property (nonatomic, strong, readonly) UIImageView *imageView;

    Swift

    var imageView: UIImageView! { get }
  • 预提供的UILabel,默认为nil,调用到的时候才初始化。默认支持多行。

    Declaration

    Objective-C

    @property (nonatomic, strong, readonly) UILabel *textLabel;

    Swift

    var textLabel: UILabel! { get }
  • 圆角矩形气泡内的padding(不包括三角箭头),默认是(8, 8, 8, 8)

    Declaration

    Objective-C

    @property (nonatomic) UIEdgeInsets contentEdgeInsets;

    Swift

    var contentEdgeInsets: UIEdgeInsets { get set }
  • 调整imageView的位置,默认为UIEdgeInsetsZero。top/left正值表示往下/右方偏移,bottom/right仅在对应位置存在下一个子View时生效(例如只有同时存在imageView和textLabel时,imageEdgeInsets.right才会生效)。

    Declaration

    Objective-C

    @property (nonatomic) UIEdgeInsets imageEdgeInsets;

    Swift

    var imageEdgeInsets: UIEdgeInsets { get set }
  • 调整textLabel的位置,默认为UIEdgeInsetsZero。top/left/bottom/right的作用同imageEdgeInsets

    Declaration

    Objective-C

    @property (nonatomic) UIEdgeInsets textEdgeInsets;

    Swift

    var textEdgeInsets: UIEdgeInsets { get set }
  • 三角箭头的大小,默认为 CGSizeMake(18, 9)

    Declaration

    Objective-C

    @property (nonatomic) CGSize arrowSize;

    Swift

    var arrowSize: CGSize { get set }
  • 最大宽度(指整个控件的宽度,而不是contentView部分),默认为CGFLOAT_MAX

    Declaration

    Objective-C

    @property (nonatomic) CGFloat maximumWidth;

    Swift

    var maximumWidth: CGFloat { get set }
  • 最小宽度(指整个控件的宽度,而不是contentView部分),默认为0

    Declaration

    Objective-C

    @property (nonatomic) CGFloat minimumWidth;

    Swift

    var minimumWidth: CGFloat { get set }
  • 最大高度(指整个控件的高度,而不是contentView部分),默认为CGFLOAT_MAX

    Declaration

    Objective-C

    @property (nonatomic) CGFloat maximumHeight;

    Swift

    var maximumHeight: CGFloat { get set }
  • 最小高度(指整个控件的高度,而不是contentView部分),默认为0

    Declaration

    Objective-C

    @property (nonatomic) CGFloat minimumHeight;

    Swift

    var minimumHeight: CGFloat { get set }
  • 计算布局时期望的默认位置,默认为QMUIPopupContainerViewLayoutDirectionAbove,也即在目标的上方

    Declaration

    Objective-C

    @property (nonatomic) QMUIPopupContainerViewLayoutDirection preferLayoutDirection;

    Swift

    var preferLayoutDirection: QMUIPopupContainerViewLayoutDirection { get set }
  • 最终的布局方向(preferLayoutDirection只是期望的方向,但有可能那个方向已经没有剩余空间可摆放控件了,所以会自动变换)

    Declaration

    Objective-C

    @property (nonatomic, readonly) QMUIPopupContainerViewLayoutDirection currentLayoutDirection;

    Swift

    var currentLayoutDirection: QMUIPopupContainerViewLayoutDirection { get }
  • 最终布局时箭头距离目标边缘的距离,默认为5

    Declaration

    Objective-C

    @property (nonatomic) CGFloat distanceBetweenSource;

    Swift

    var distanceBetweenSource: CGFloat { get set }
  • 最终布局时与父节点的边缘的临界点,默认为(10, 10, 10, 10)

    Declaration

    Objective-C

    @property (nonatomic) UIEdgeInsets safetyMarginsOfSuperview;

    Swift

    var safetyMarginsOfSuperview: UIEdgeInsets { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, strong) UIColor *backgroundColor

    Swift

    var backgroundColor: UIColor! { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, strong) UIColor *highlightedBackgroundColor

    Swift

    var highlightedBackgroundColor: UIColor! { get set }
  • 当使用方法 2 显示并且打开了 automaticallyHidesWhenUserTap 时,可修改背景遮罩的颜色,默认为 UIColorMask,若非使用方法 2,或者没有打开 automaticallyHidesWhenUserTap,则背景遮罩为透明(可视为不存在背景遮罩)

    Declaration

    Objective-C

    @property (nonatomic, strong) UIColor *maskViewBackgroundColor;

    Swift

    var maskViewBackgroundColor: UIColor! { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, strong) UIColor *shadowColor

    Swift

    var shadowColor: UIColor! { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, strong) UIColor *borderColor

    Swift

    var borderColor: UIColor! { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, assign) CGFloat borderWidth

    Swift

    var borderWidth: CGFloat { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, assign) CGFloat cornerRadius

    Swift

    var cornerRadius: CGFloat { get set }
  • 可以是 UINavigationBar、UIToolbar 上的 UIBarButtonItem,或者 UITabBar 上的 UITabBarItem

    Declaration

    Objective-C

    @property (nonatomic, weak) __kindof UIBarItem *sourceBarItem;

    Swift

    weak var sourceBarItem: UIBarItem! { get set }
  • Undocumented

    Declaration

    Objective-C

    @property(nonatomic, weak) __kindof UIView *sourceView

    Swift

    weak var sourceView: UIView! { get set }
  • rect 需要处于 QMUIPopupContainerView 所在的坐标系内,例如如果 popup 使用 addSubview: 的方式添加到界面,则 sourceRect 应该是 superview 坐标系内的;如果 popup 使用 window 的方式展示,则 sourceRect 需要转换为 window 坐标系内。

    Declaration

    Objective-C

    @property (nonatomic) CGRect sourceRect;

    Swift

    var sourceRect: CGRect { get set }
  • 立即刷新当前 popup 的布局,前提是 popup 已经被 show 过。

    Declaration

    Objective-C

    - (void)updateLayout;

    Swift

    func updateLayout()
  • Undocumented

    Declaration

    Objective-C

    - (void)showWithAnimated:(BOOL)animated;

    Swift

    func showWith(animated: Bool)
  • Undocumented

    Declaration

    Objective-C

    - (void)showWithAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion;

    Swift

    func showWith(animated: Bool, completion: ((Bool) -> Void)!)
  • Undocumented

    Declaration

    Objective-C

    - (void)hideWithAnimated:(BOOL)animated;

    Swift

    func hideWith(animated: Bool)
  • Undocumented

    Declaration

    Objective-C

    - (void)hideWithAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion;

    Swift

    func hideWith(animated: Bool, completion: ((Bool) -> Void)!)
  • Undocumented

    Declaration

    Objective-C

    - (BOOL)isShowing;

    Swift

    func isShowing() -> Bool
  • 即将显示时的回调 注:如果需要使用例如 didShowBlock 的时机,请使用 @showWithAnimated:completion: 的 completion 参数来实现。 @argv animated 是否需要动画

    Declaration

    Objective-C

    @property (nonatomic, copy) void (^)(BOOL) willShowBlock;

    Swift

    var willShowBlock: ((Bool) -> Void)! { get set }
  • 即将隐藏时的回调 @argv hidesByUserTap 用于区分此次隐藏是否因为用户手动点击空白区域导致浮层被隐藏 @argv animated 是否需要动画

    Declaration

    Objective-C

    @property (nonatomic, copy) void (^)(BOOL, BOOL) willHideBlock;

    Swift

    var willHideBlock: ((Bool, Bool) -> Void)! { get set }
  • 已经隐藏后的回调 @argv hidesByUserTap 用于区分此次隐藏是否因为用户手动点击空白区域导致浮层被隐藏

    Declaration

    Objective-C

    @property (nonatomic, copy) void (^)(BOOL) didHideBlock;

    Swift

    var didHideBlock: ((Bool) -> Void)! { get set }

UISubclassingHooks

  • 子类重写,在初始化时做一些操作

    Declaration

    Objective-C

    - (void)didInitialize;

    Swift

    func didInitialize()
  • 子类重写,告诉父类subviews的合适大小

    Declaration

    Objective-C

    - (CGSize)sizeThatFitsInContentView:(CGSize)size;

    Swift

    func sizeThatFits(inContentView size: CGSize) -> CGSize

    Parameters

    size

    浮层里除去 safetyMarginsOfSuperview、arrowSize、contentEdgeInsets 之外后,留给内容的实际大小,计算 subview 大小时均应使用这个参数来计算

    Return Value

    自定义内容实际占据的大小