CNPGridMenu 菜单源码分析
嗯,就是这么一个Menu。今天就来看看到底怎么实现的。
控件里面有几个部分
基本上对应上MVC模式
- CNPGridMenu(controller)
- CNPGridMenuCell(view)
- CNPGridMenuItem(model)
CNPGridMenuItem
@interface CNPGridMenuItem : NSObject
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) UIImage *icon;
@property (nonatomic, copy) SelectionHandler selectionHandler;
@end
对应Menu里面各选项的标题、icon以及对应操作handler。
CNPGridMenuCell
@property (nonatomic, strong) UIVisualEffectView *vibrancyView;
- (void)setupCell {
UIVisualEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:[UIBlurEffect effectWithStyle:self.blurEffectStyle]];
self.vibrancyView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
[self.contentView addSubview:self.vibrancyView];
...
[self.vibrancyView.contentView addSubview:self.circleButton];
...
[self.vibrancyView.contentView addSubview:self.iconView];
...
[self.vibrancyView.contentView addSubview:self.titleLabel];
}
vibrancyView的effect是UIVibrancyEffect,它的作用是将添加到它身上的控件进行高亮处理,如下图,每个cell里面的控件,都能看见背景图对应处的高亮效果。
CNPGridMenu
CNPGridMenu是一个UICollectionViewController。
里面值得留意的是在CNPGridMenuFlowLayout里面处理cell垂直居中效果
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray* array = [super layoutAttributesForElementsInRect:rect];
//将cell 往下移动居中
UICollectionViewLayoutAttributes* att = [array lastObject];
if (att){
CGFloat lastY = att.frame.origin.y + att.frame.size.height;//最后一个cell的maxY值
CGFloat diff = self.collectionView.frame.size.height - lastY;//计算最后一个cell离collectionView底部距离
if (diff > 0){
//如果距离大于0,则将collectionView的contentInset 下移距离的一半,达到居中效果
UIEdgeInsets contentInsets = UIEdgeInsetsMake(diff/2, 0.0, 0.0, 0.0);
self.collectionView.contentInset = contentInsets;
}
}
return array;
}
Menu的事件响应链
- CNPGridMenu的弹出和收起,都是由ViewController控制,自己不进行这方面的操作;
- CNPGridMenu的CNPGridMenuDelegate是ViewController;
- CNPGridMenu的blurView添加一个手势,单击的时候会触发-gridMenuDidTapOnBackground代理方法(ViewController在这个代理方法中实现dismiss功能);
- CNPGridMenuCell的CNPGridMenuButtonDelegate是CNPGridMenu;
- CNPGridMenuCell里面的circleButton的UIControlEventTouchUpInside点击事件会触发didTapOnGridMenuItem:代理方法(CNPGridMenu在这个代理方法中实现自己的代理方法gridMenu:didTapOnItem:的执行)和执行menuItem的selectionHandler的处理;
UIViewController (CNPGridMenu)
最后,添加一个UIViewController的分类(CNPGridMenu),对所有的UIViewController提供girdMenu属性以及present和dismiss方法;
@interface UIViewController (CNPGridMenu)
@property (nonatomic, strong) CNPGridMenu *gridMenu;
- (void)presentGridMenu:(CNPGridMenu *)menu animated:(BOOL)flag completion:(void (^)(void))completion;
- (void)dismissGridMenuAnimated:(BOOL)flag completion:(void (^)(void))completion;
@end
@implementation UIViewController (CNPGridMenu)
@dynamic gridMenu;
- (void)presentGridMenu:(CNPGridMenu *)menu animated:(BOOL)flag completion:(void (^)(void))completion {
[menu setModalPresentationStyle:UIModalPresentationCustom];
[menu setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
menu.modalPresentationCapturesStatusBarAppearance = YES;
[self presentViewController:menu animated:flag completion:completion];
}
- (void)dismissGridMenuAnimated:(BOOL)flag completion:(void (^)(void))completion {
[self dismissViewControllerAnimated:flag completion:completion];
}
- (void)setGridMenu:(CNPGridMenu *)gridMenu {
objc_setAssociatedObject(self, @selector(gridMenu), gridMenu, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (CNPGridMenu *)gridMenu {
return objc_getAssociatedObject(self, @selector(gridMenu));
}
@end