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>