MMPopupView 源码分析
az6659208
8年前
<h2><strong>MMPopupView 是什么</strong></h2> <p>MMPopupView 是一个基于UIWindow的一个简洁、高效的弹出框组件,其中包括 :MMAlertView & MMSheetView 这两种内置的展示效果,另外开发者也可以自定义弹出视图以及相关事务;</p> <pre> <code class="language-objectivec">typedef NS_ENUM(NSUInteger, MMPopupType) { MMPopupTypeAlert, MMPopupTypeSheet, MMPopupTypeCustom, };</code></pre> <h2><strong>来源</strong></h2> <p> </p> <p><img src="https://simg.open-open.com/show/2183b35085682b59ba5316295559f7a2.jpg"></p> <h2><strong>MMPopupView 适用场景</strong></h2> <p>MMPopupView 可适用于照片选择、时间选择、性别选择弹出框以及提示、警告、以及自定义弹出框的搭建;</p> <h2><strong>MMPopupView 工作逻辑</strong></h2> <ol> <li> <p>层次结构</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/05b9dd5d3c5ed01ced51cb819ef6692b.png"></p> </li> <li>MMPopupView 是基于 UIWindow 展现弹出框的基类的,但与相对直接基于当前 UIWindow 来说,它用 MMPopupWindow 这个UIWindow 单例来显示或隐藏所有弹出框的容器,并处理相关的手势事件;</li> <li>MMPopupView 通过 维护和管理 MMPopupItem 结构数组 来管理包括 高亮、颜色、是否可用、标题、block等单元属性;</li> <li>MMPopupView 使用自己的 Category 来完成最后的 视图填充、视图动画、视图约束、背景颜色、UIWindow 隐藏和展示等;</li> </ol> <pre> <code class="language-objectivec">@property (nonatomic, strong, readonly ) UIView *mm_dimBackgroundView; @property (nonatomic, assign, readonly ) BOOL mm_dimBackgroundAnimating; @property (nonatomic, assign ) NSTimeInterval mm_dimAnimationDuration; @property (nonatomic, strong, readonly ) UIView *mm_dimBackgroundBlurView; @property (nonatomic, assign ) BOOL mm_dimBackgroundBlurEnabled; @property (nonatomic, assign ) UIBlurEffectStyle mm_dimBackgroundBlurEffectStyle; - (void) mm_showDimBackground; - (void) mm_hideDimBackground; - (void) mm_distributeSpacingHorizontallyWith:(NSArray*)view; - (void) mm_distributeSpacingVerticallyWith:(NSArray*)view;</code></pre> <h2><strong>MMPopupView 实现细节</strong></h2> <ul> <li> <p>MMPopupWindow的定义如下:</p> </li> </ul> <pre> <code class="language-objectivec">@interface MMPopupWindow : UIWindow @property (nonatomic, assign) BOOL touchWildToHide; // default is NO. When YES, popup views will be hidden when user touch the translucent background. @property (nonatomic, readonly) UIView* attachView; + (MMPopupWindow *)sharedWindow; /** * cache the window to prevent the lag of the first showing. */ - (void) cacheWindow; @end</code></pre> <p>很简单 只有一个属性 touchWildToHide 用来控制是否可以点击非弹出框的地方来使弹出框消失 还有一个 cacheWindow 的方法来预加载 MMPopupWindow 防止第一次使用的时候顿卡;</p> <ul> <li> <p>MMPopupWindow 定义如下:</p> </li> </ul> <pre> <code class="language-objectivec">typedef NS_ENUM(NSUInteger, MMPopupType) { MMPopupTypeAlert, MMPopupTypeSheet, MMPopupTypeCustom, }; @class MMPopupView; typedef void(^MMPopupBlock)(MMPopupView *); typedef void(^MMPopupCompletionBlock)(MMPopupView *, BOOL); @interface MMPopupView : UIView @property (nonatomic, assign, readonly) BOOL visible; // default is NO. @property (nonatomic, strong ) UIView *attachedView; // default is MMPopupWindow. You can attach MMPopupView to any UIView. @property (nonatomic, assign ) MMPopupType type; // default is MMPopupTypeAlert. @property (nonatomic, assign ) NSTimeInterval animationDuration; // default is 0.3 sec. @property (nonatomic, assign ) BOOL withKeyboard; // default is NO. When YES, alert view with be shown with a center offset (only effect with MMPopupTypeAlert). @property (nonatomic, copy ) MMPopupCompletionBlock showCompletionBlock; // show completion block. @property (nonatomic, copy ) MMPopupCompletionBlock hideCompletionBlock; // hide completion block @property (nonatomic, copy ) MMPopupBlock showAnimation; // custom show animation block. @property (nonatomic, copy ) MMPopupBlock hideAnimation; // custom hide animation block. /** * override this method to show the keyboard if with a keyboard */ - (void) showKeyboard; /** * override this method to hide the keyboard if with a keyboard */ - (void) hideKeyboard; /** * show the popup view */ - (void) show; /** * show the popup view with completiom block * * @param block show completion block */ - (void) showWithBlock:(MMPopupCompletionBlock)block; /** * hide the popup view */ - (void) hide; /** * hide the popup view with completiom block * * @param block hide completion block */ - (void) hideWithBlock:(MMPopupCompletionBlock)block; /** * hide all popupview with current class, eg. [MMAlertview hideAll]; */ + (void) hideAll; @end</code></pre> <p>包括,PopupView 类型、时间、可见、show 与 hide 的block 事件等;</p> <p>其中 - hideWithBlock 和 hideWithBlock 展现和隐藏 外部方法,并通过</p> <p>其中 PopupView 内部也包含 alert、sheet、custom 的展现于隐藏的动画事务;</p> <pre> <code class="language-objectivec">- (MMPopupBlock)alertShowAnimation - (MMPopupBlock)alertHideAnimation - (MMPopupBlock)sheetShowAnimation - (MMPopupBlock)sheetHideAnimation - (MMPopupBlock)customShowAnimation - (MMPopupBlock)customHideAnimation</code></pre> <p>以 alertShowAnimation 为例:</p> <pre> <code class="language-objectivec">- (MMPopupBlock)alertShowAnimation { MMWeakify(self); MMPopupBlock block = ^(MMPopupView *popupView){ MMStrongify(self); if ( !self.superview ) { [self.attachedView.mm_dimBackgroundView addSubview:self]; [self mas_updateConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.attachedView).centerOffset(CGPointMake(0, self.withKeyboard?-216/2:0)); }]; [self layoutIfNeeded]; } self.layer.transform = CATransform3DMakeScale(1.2f, 1.2f, 1.0f); self.alpha = 0.0f; [UIView animateWithDuration:self.animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState animations:^{ self.layer.transform = CATransform3DIdentity; self.alpha = 1.0f; } completion:^(BOOL finished) { if ( self.showCompletionBlock ) { self.showCompletionBlock(self, finished); } }]; }; return block; }</code></pre> <p>添加到 将popupView 添加到 _attachedView 更新约束、渲染 并 执行相关动画;</p> <ul> <li> <p>MMPopupView 核心拓展类定义如下:</p> </li> </ul> <pre> <code class="language-objectivec">@interface UIView (MMPopup) @property (nonatomic, strong, readonly ) UIView *mm_dimBackgroundView; @property (nonatomic, assign, readonly ) BOOL mm_dimBackgroundAnimating; @property (nonatomic, assign ) NSTimeInterval mm_dimAnimationDuration; @property (nonatomic, strong, readonly ) UIView *mm_dimBackgroundBlurView; @property (nonatomic, assign ) BOOL mm_dimBackgroundBlurEnabled; @property (nonatomic, assign ) UIBlurEffectStyle mm_dimBackgroundBlurEffectStyle; - (void) mm_showDimBackground; - (void) mm_hideDimBackground; - (void) mm_distributeSpacingHorizontallyWith:(NSArray*)view; - (void) mm_distributeSpacingVerticallyWith:(NSArray*)view; @end</code></pre> <p>包括 MMPopWindow 已经 MMPopupView 的 subviews 新增、相关约束以及hide & show,这是整个类库执行的最终落脚点</p> <h2><strong>MMPopupView 几点启示</strong></h2> <p>MMPopupView 源码本身并不复杂,但仍然有许多可以借鉴学习的地方:</p> <ul> <li> <p>Objective-C Associated Objects 的实现原理<br> <a href="/misc/goto?guid=4958960888885871374" rel="nofollow,noindex">http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/</a></p> </li> <li> <p>如何自定义PopupView</p> </li> <li> <p>二次封装了 MMPopupView 的 简单用法,参考如下:<br> UIViewController+MAC.h</p> </li> </ul> <pre> <code class="language-objectivec">@interface UIViewController(MAC) #pragma mark 设置 展示AlertView or SheetView /** * 展示Alert提示信息 */ -(void)showAlertMessage:(NSString*)message; /** * 展示Alert提示信息 */ -(void)showAlertMessage:(NSString*)message titile:(NSString *)title; /** * 展示Alert提示信息 * * @param message 提示内容 * @param title 提示标题 * @param clickArr 按钮信息数组 * @param clickIndex 点击的下标 */ -(void)showAlertMessage:(NSString *)message title:(NSString *)title clickArr:(NSArray *)arr click:(MMPopupItemHandler) clickIndex; /** * 展示SheetView提示信息 * * @param title 提示标题 * @param clickArr 按钮信息数组 * @param clickIndex 点击的下标 */ -(void)showSheetTitle:(NSString *)title clickArr:(NSArray *)arr click:(MMPopupItemHandler) clickIndex;</code></pre> <p>UIViewController+MAC.m</p> <pre> <code class="language-objectivec">@implementation UIViewController(MAC) -(void)showAlertMessage:(NSString*)message { [self showAlertMessage:message titile:@"提示"]; } -(void)showAlertMessage:(NSString*)message titile:(NSString *)title{ MMPopupItemHandler block = ^(NSInteger index){ NSLog(@"clickd %@ button",@(index)); }; NSArray *items = @[MMItemMake(@"确定", MMItemTypeHighlight, block)]; MMAlertView *alertView = [[MMAlertView alloc] initWithTitle:title detail:message items:items]; [alertView show]; } -(void)showAlertMessage:(NSString *)message title:(NSString *)title clickArr:(NSArray *)arr click:(MMPopupItemHandler) clickIndex{ if (!arr||arr.count<=0) { return; } __block NSMutableArray *items=[NSMutableArray array]; [arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [items addObject:MMItemMake(obj, MMItemTypeHighlight, clickIndex)]; }]; MMAlertView *alertView = [[MMAlertView alloc] initWithTitle:title detail:message items:items]; [alertView show]; } -(void)showSheetTitle:(NSString *)title clickArr:(NSArray *)arr click:(MMPopupItemHandler)clickIndex{ if (!arr||arr.count<=0) { return; } __block NSMutableArray *items=[NSMutableArray array]; [arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [items addObject:MMItemMake(obj, MMItemTypeHighlight, clickIndex)]; }]; [[[MMSheetView alloc] initWithTitle:title items:items] show]; };</code></pre> <p> </p> <p>来自:http://www.jianshu.com/p/c5ebd455ee82</p> <p> </p>