最后效果图:

各控件关系图1:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">\

各控件关系图2:

点击Dock上面的buttonDockItem,

创建经导航控制器包装的DealListController,

而且加入到主控制器的右側空间

//
// DealListController.m
// 帅哥_团购
//
// Created by beyond on 14-8-14.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击dock上面的【团购】button相应的控制器,上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部由三个小button组成<TopMenuItem>) #import "DealListController.h"
// 导航栏左边是一个大button(顶部菜单)
#import "TopMenu.h" @interface DealListController () @end @implementation DealListController - (void)viewDidLoad
{
[super viewDidLoad];
// 1,设置上方的导航栏,右边是搜索bar,左边是一个大的VIEW(内有三个button),即TopMenu,内部的button是TopMenuItem
[self addNaviBarBtn]; }
// 1,设置上方的导航栏,右边是搜索bar,左边是一个大的VIEW(内有三个button),<span style="font-family: Arial, Helvetica, sans-serif;">即TopMenu,内部的button是TopMenuItem</span>
- (void)addNaviBarBtn
{ // 1.右边的搜索框
UISearchBar *s = [[UISearchBar alloc] init];
s.frame = CGRectMake(0, 0, 210, 35);
s.placeholder = @"请输入商品名、地址等";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:s]; // 2.左边的菜单条,导航栏左边是一个大button(顶部菜单)
TopMenu *topMenu = [[TopMenu alloc] init];
// 3.用于点击顶部button时,容纳创建出来的底部弹出菜单(包含一个contentView和cover,contentView又包含scrollView和subTitleImgView),本成员是由创建此TopMenu的外部赋值传入, 这里是控制器的view,就是导航栏以下的全部区域
// 重要~~~~~~~~~~
topMenu.controllerView = self.view; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:topMenu];
}
@end

TopMenu.h

//
// TopMenu.h
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击dock上面的【团购】button时,创建出相应的经导航包装后的子控制器,子控制器的上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部仅仅由三个小button组成<TopMenuItem>它们各自是:所有频道,所有商区,默认排序),点击TopMenu中的某一个button<TopMenuItem>,会在其下方,弹出一个PopMenu,PopMenu包含二个部分(上面是一个contentView:包含:scrollView和subTitleImgView,下:蒙板) #import <UIKit/UIKit.h> @interface TopMenu : UIView // 用于点击顶部菜单项时,容纳创建出来的底部弹出菜单(包含一个contentView和cover,contentView又包含scrollView和subTitleImgView),本成员是由创建此TopMenu的控制器赋值传入, 本成员属性是用来接收控制器的view,就是导航栏以下的所有区域,目的是用于加入并展示PopMenu
@property (nonatomic, weak) UIView *controllerView;
@end

TopMenu.m

负责创建和加入3个TopMenuItem,

监听其内部三个TopMenuItem的点击事件,

而且依据点击的button的tag不同,创建出不同的PopMenu,并控制PopMenu的出现和隐藏,最后一个是注冊到通知中心,监听全部通知,而且设置三个TopMenuItem的显示文字

//
// TopMenu.m
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击dock上面的【团购】button相应的控制器,上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部仅仅由三个小button组成<TopMenuItem>它们各自是:所有频道,所有商区,默认排序),点击TopMenu中的某一个button<TopMenuItem>,会在其下方,弹出一个PopMenu,PopMenu包含二个部分(上面是一个contentView:包含:scrollView和subTitleImgView,下:蒙板) #import "TopMenu.h" #import "TopMenuItem.h"
#import "CategoryPopMenu.h"
#import "DistrictPopMenu.h"
#import "OrderPopMenu.h"
#import "MetaDataTool.h"
#import "Order.h" @interface TopMenu()
{
// 三个顶部菜单项中当前选中的那一个,三个button:所有分类,所有商区,默认排序
TopMenuItem *_currentTopMenuItem; // 在点击不同的顶部菜单项的时候,由于要控制其相应的底部弹出菜单的出现和隐藏,因此,在创建它时,就要用成员变量,记住 弹出的分类菜单
CategoryPopMenu *_categoryPopMenu;
// 在点击不同的顶部菜单项的时候,由于要控制其相应的底部弹出菜单的出现和隐藏,因此,在创建它时,就要用成员变量,记住 弹出的区域菜单
DistrictPopMenu *_districtPopMenu;
// 在点击不同的顶部菜单项的时候,由于要控制其相应的底部弹出菜单的出现和隐藏,因此,在创建它时,就要用成员变量,记住 弹出的排序菜单
OrderPopMenu *_orderPopMenu; // 正在展示的底部弹出菜单,是个父类
PopMenu *_showingPopMenu; // 由于要更改其显示文字,所以要成员变量记住创建出来的 分类菜单项
TopMenuItem *_categoryTopMenuItem;
// 由于要更改其显示文字,所以要成员变量记住创建出来的 区域菜单项
TopMenuItem *_districtTopMenuItem;
// 由于要更改其显示文字,所以要成员变量记住创建出来的 排序菜单项
TopMenuItem *_orderTopMenuItem;
}
@end @implementation TopMenu - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) { // 1.加入一个button:所有分类 (由于要更改其显示文字,所以要成员变量记住)
_categoryTopMenuItem = [self addMenuItem:kAllCategory index:0]; // 2.加入一个button:所有商区 (由于要更改其显示文字,所以要成员变量记住)
_districtTopMenuItem = [self addMenuItem:kAllDistrict index:1]; // 3.加入一个button:默认排序 (由于要更改其显示文字,所以要成员变量记住)
_orderTopMenuItem = [self addMenuItem:@"默认排序" index:2]; // 4.顶部菜单须要注冊并监听所有通知,目的是更改其里面菜单项的文字,而且控制弹出菜单的显示和隐藏
kAddAllNotes(dataChange)
}
return self;
}
// 5,抽取的方法,加入一个顶部菜单项(TopMenuItem),三个button:所有分类,所有商区,默认排序
- (TopMenuItem *)addMenuItem:(NSString *)title index:(int)index
{
TopMenuItem *item = [[TopMenuItem alloc] init];
item.title = title;
item.tag = index;
// 三个button:所有分类,所有商区,默认排序 水平排列
item.frame = CGRectMake(kTopMenuItemW * index, 0, 0, 0);
// 重要~~~顶部button(三个button:所有分类,所有商区,默认排序)被点击之后,都会调用此方法,依据tag进行区分,以便弹出不同的PopMenu
[item addTarget:self action:@selector(topMenuItemClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:item];
return item;
} // 4.注冊并监听所有通知时调用此方法,更改button的文字,控制弹出菜单显示和隐藏
- (void)dataChange
{
// 0.取消当前TopMenuItem的选中状态,而且置空_currentTopMenuItem ,由于监听到通知时,肯定是用户点击了一个子标题button,或者一个没有子标题的PopMenuItem
_currentTopMenuItem.selected = NO;
_currentTopMenuItem = nil; // 1.设置 分类button 要显示的文字,从工具中获得
NSString *c = [MetaDataTool sharedMetaDataTool].currentCategoryName;
if (c) {
_categoryTopMenuItem.title = c;
} // 2.设置 商区button 要显示的文字,从工具中获得
NSString *d = [MetaDataTool sharedMetaDataTool].currentDistrictName;
if (d) {
_districtTopMenuItem.title = d;
} // 3.设置 排序button 要显示的文字,从工具中获得
NSString *o = [MetaDataTool sharedMetaDataTool].currentOrder.name;
if (o) {
_orderTopMenuItem.title = o;
} // 4.最后,调用正在显示的弹出菜单的方法:隐藏底部弹出菜单,并置空正在显示的弹出菜单
[_showingPopMenu hidePopMenu];
_showingPopMenu = nil;
} // 5-1,重要~~~监听顶部菜单项的点击,
// 顶部button(三个button:所有分类,所有商区,默认排序)被点击之后,都会调用此方法,依据tag进行区分,以便弹出不同的PopMenu
- (void)topMenuItemClick:(TopMenuItem *)item
{
// 0.假设还没有选择好城市,则不同意点击顶部菜单button
if ([MetaDataTool sharedMetaDataTool].currentCity == nil) return;
// 1.控制选中状态的切换,先把前面记住的当前顶部菜单项取消选中
_currentTopMenuItem.selected = NO;
// 假设两次点击的是同一个顶部菜单项,则隐藏掉弹出的菜单,而且置空当前的选中TopMenuItem
if (_currentTopMenuItem == item) {
_currentTopMenuItem = nil;
// 隐藏底部菜单
[self hidePopMenu];
} else {
// 假设两次点击的是是不同的顶部菜单项,将TopMenuItem置为选中状态,且用成员变量记住,而且展示相应的底部弹出菜单
item.selected = YES;
_currentTopMenuItem = item;
// 显示与顶部菜单项,相应的底部弹出菜单
[self showPopMenu:item];
}
}
// 5-2,点击了同样的TopMenuItem,要隐藏底部已经弹出的菜单,而且置其为空
- (void)hidePopMenu
{
// 调用_showingPopMenu其自己的方法,隐藏并移除其内部的contentView(包含scrollView和subTitleImgView,并置cover透明),并置空_showingPopMenu
[_showingPopMenu hidePopMenu];
_showingPopMenu = nil;
} // 5-3,点击了不同的TopMenuItem,要显示相应的底部弹出菜单
- (void)showPopMenu:(TopMenuItem *)item
{
// 1,先推断 是否须要让弹出菜单运行出场动画(没有正在展示的弹出菜单时,才须要运行出场动画)
BOOL animted;
// 假设有正在显示的弹出菜单,如切换button点击的时候
if (_showingPopMenu) {
// 1-1,先移除当前正在显示的弹出菜单
[_showingPopMenu removeFromSuperview];
// 1-2,不要动画出场
animted = NO;
}else{
// 没有正在展示的弹出菜单时,才须要运行出场动画
animted = YES;
} // 2,依据点击的顶部菜单项的tag,创建并显示不同的底部弹出菜单(三个button:所有分类,所有商区,默认排序)
if (item.tag == 0) {
// 创建分类弹出菜单,而且用成员记住,且置其为正在展示的PopMenu
if (_categoryPopMenu == nil) {
_categoryPopMenu = [[CategoryPopMenu alloc] init];
}
_showingPopMenu = _categoryPopMenu;
} else if (item.tag == 1) { // 区域
// 创建商区弹出菜单,而且用成员记住,且置其为正在展示的PopMenu
if (_districtPopMenu == nil) {
_districtPopMenu = [[DistrictPopMenu alloc] init];
}
_showingPopMenu = _districtPopMenu;
} else {
// 创建 排序弹出菜单,而且用成员记住,且置其为正在展示的PopMenu
if (_orderPopMenu == nil) {
_orderPopMenu = [[OrderPopMenu alloc] init];
}
_showingPopMenu = _orderPopMenu;
} // 创建出来相应的底部弹出菜单后,就要设置_showingPopMenu 的frame
// _showingPopMenu.frame = _controllerView.bounds; // PopMenu 占领导航栏以下所有的空间
_showingPopMenu.frame = (CGRect){0,kTopMenuItemH-1,_controllerView.bounds.size.width,_controllerView.bounds.size.height- kTopMenuItemH}; // 设置创建出来的PopMenu的block回调,传递的是XXXPopMenu隐藏后,顶部菜单要做的事情如更改顶部的TopMenu的button选中状态
__unsafe_unretained TopMenu *menu = self;
_showingPopMenu.hideBlock = ^{
// 重要~~~当_showingPopMenu隐藏后,要更改顶部的TopMenu的button选中状态 // 1.取消当前的TopMenuItem的选中状态,并置空
menu->_currentTopMenuItem.selected = NO;
menu->_currentTopMenuItem = nil; // 2._showingPopMenu隐藏后,就要清空_showingPopMenu
menu->_showingPopMenu = nil;
}; // 加入创建出来的即将要显示的弹出菜单 到_controllerView(即导航栏以下的所有空间都是弹出菜单的)
[_controllerView addSubview:_showingPopMenu]; // 运行刚才创建出来的底部弹出菜单的 出场动画,注意:仅仅有没有正在展示的弹出菜单时,才须要运行出场动画)
if (animted) {
[_showingPopMenu showPopMenu];
}
} // 顶部菜单宽度固定是三个button的宽高,由于仅仅有三个button:所有分类,所有商区,默认排序
- (void)setFrame:(CGRect)frame
{
frame.size = CGSizeMake(3 * kTopMenuItemW, kTopMenuItemH);
[super setFrame:frame];
}
// 顶部菜单由于要改变其三个button的文字,因此在通知中心注冊成为了监听者,因此dealloc时要在通知中心,移除掉监听者
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

TopMenuItem 顶部菜单项button

//
// TopMenuItem.m
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击dock上面的【团购】button相应的控制器,上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部仅仅由三个小button组成<TopMenuItem>它们各自是:所有频道,所有商区,默认排序),点击TopMenu中的某一个button<TopMenuItem>,会在其下方,弹出一个PopMenu,PopMenu包含二个部分(上面是一个contentView:包含:scrollView和subTitleImgView,下:蒙板) #import <UIKit/UIKit.h> @interface TopMenuItem : UIButton
// 设置buttonTopMenuItem显示的文字
@property (nonatomic, copy) NSString *title;
@end // 左文字 如所有分类 、所有商区、默认排序
#define kTitleScale 0.8
@implementation TopMenuItem - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1.文字颜色
[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.titleLabel.font = [UIFont systemFontOfSize:15]; // 2.设置button右边的箭头
[self setImage:[UIImage imageNamed:@"ic_arrow_down.png"] forState:UIControlStateNormal];
self.imageView.contentMode = UIViewContentModeCenter; // 3.设置button右边的切割线
UIImage *img = [UIImage imageNamed:@"separator_topbar_item.png"];
UIImageView *divider = [[UIImageView alloc] initWithImage:img];
divider.bounds = CGRectMake(0, 0, 2, kTopMenuItemH * 0.7);
divider.center = CGPointMake(kTopMenuItemW, kTopMenuItemH * 0.5);
[self addSubview:divider]; // 4.设置button选中时的背景
[self setBackgroundImage:[UIImage imageStretchedWithName:@"slider_filter_bg_normal.png"] forState:UIControlStateSelected];
}
return self;
} - (void)setTitle:(NSString *)title
{
_title = title; [self setTitle:title forState:UIControlStateNormal];
}
// 自己固定顶部菜单项button的好宽高
- (void)setFrame:(CGRect)frame
{
frame.size = CGSizeMake(kTopMenuItemW, kTopMenuItemH);
[super setFrame:frame];
}
// 左文字的frame
- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
CGFloat h = contentRect.size.height;
CGFloat w = contentRect.size.width * kTitleScale;
return CGRectMake(0, 0, w, h);
}
// 右图片的frame
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGFloat h = contentRect.size.height;
CGFloat x = contentRect.size.width * kTitleScale;
CGFloat w = contentRect.size.width - x;
return CGRectMake(x, 0, w, h);
} @end

PopMenu.h

//
// PopMenu.h
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// PopMenu是点击顶部button项,在其下方弹出的菜单的父类,成员有:以下是一个cover蒙板,上面是一个contentView(包括着scrollView和subTitleImgView,当中scrollView里面放的全是PopMenuItem,如美食,如海淀区.....subTitleImgView里面放的全是美食以下的全部子标题,如川菜 湘菜 粤菜) // 点击dock上面的【团购】button,创建一个经过导航包装的DealList控制器,控制器的上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部由三个小button组成<TopMenuItem>),点击TopMenu中的某一个button<TopMenuItem>,会在其下方,弹出一个PopMenu,PopMenu包括二个部分,见上面 #import <UIKit/UIKit.h>
#import "SubTitleImgViewDelegate.h"
@class SubTitleImgView, PopMenuItem; @interface PopMenu : UIView <SubTitleImgViewDelegate>
{
// 以下成员是开放给子类訪问和改动的 // 容纳全部的分类或商区,如美食,如海淀区
UIScrollView *_scrollView; // 容纳全部子标题的ImgView,里面全是一个个button,如美食以下的川菜、湘菜、粤菜等
SubTitleImgView *_subTitleImgView; // item的父类,弹出菜单项:记录当前选中的菜单项,如美食,如海淀区(此是父类)
PopMenuItem *_selectedPopMenuItem;
} // 弹出菜单隐藏完成之后,要通知顶部菜单
@property (nonatomic, copy) void (^hideBlock)(); // 供外部调用,通过动画显示 PopMenu
- (void)showPopMenu;
// 供外部调用,通过动画隐藏 PopMenu
- (void)hidePopMenu; @end

PopMenu.m



创建并加入一个cover,一个ContentView,并向ContentView加入一个ScrollView



//
// PopMenu.m
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// PopMenu是点击顶部button项,在其下方弹出的菜单的父类,成员有:以下是一个cover蒙板,上面是一个contentView(包含着scrollView和subTitleImgView,当中scrollView里面放的全是PopMenuItem,如美食,如海淀区.....subTitleImgView里面放的全是美食以下的全部子标题,如川菜 湘菜 粤菜) // 点击dock上面的【团购】button,创建一个经过导航包装的DealList控制器,控制器的上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部由三个小button组成<TopMenuItem>),点击TopMenu中的某一个button<TopMenuItem>,会在其下方,弹出一个PopMenu,PopMenu包含二个部分,见上面 #import "PopMenu.h"
// 遮罩
#import "Cover.h" // subTitleImgView里面放的全是美食以下的全部子标题,如川菜湘菜粤菜
#import "SubTitleImgView.h"
// scrollView里面放的全是PopMenuItem,如美食,如海淀区
#import "PopMenuItem.h"
#import "MetaDataTool.h" #import "CategoryPopMenuItem.h"
#import "DistrictPopMenuItem.h"
#import "OrderPopMenuItem.h" @interface PopMenu()
{
// 上面是_contentView,包含scrollView和subTitleImgView,当中scrollView里面放的全是PopMenuItem,如美食,如海淀区.....subTitleImgView里面放的全是美食以下的全部子标题,如川菜湘菜粤菜
UIView *_contentView;
// 遮罩
Cover *_cover; }
@end @implementation PopMenu - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 0.为适应横竖屏的变化,设置self PopMenu宽高自己主动伸缩
self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; // 1.加入蒙板(遮盖),而且点击蒙板后,隐藏并移除popMenu的contentView(内部是scrollView和subTitleImgView),并置cover透明
[self addCover]; // 2.加入内容view(内部是scrollView和subTitleImgView)
[self addContentView]; // 3.加入ScrollView到contentView
[self addScrollView]; }
return self;
} // 1.加入蒙板(遮盖),而且点击蒙板后,隐藏并移除popMenu的contentView(内部是scrollView和subTitleImgView),并置cover透明
- (void)addCover
{
_cover = [Cover coverWithTarget:self action:@selector(hideContentView)];
_cover.frame = self.bounds;
[self addSubview:_cover];
} // 2.加入内容view(内部是scrollView和subTitleImgView)
- (void)addContentView
{ _contentView = [[UIView alloc] init];
// 默认高度是一个popMenuItem高度
_contentView.frame = CGRectMake(0, 0, self.frame.size.width, kBottomMenuItemH);
// 宽度伸缩,可是高度其内部通过数据源的多少自己主动计算行数及总高度
_contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self addSubview:_contentView];
} // 3.加入ScrollView到contentView
-(void)addScrollView
{
_scrollView = [[UIScrollView alloc] init];
_scrollView.showsHorizontalScrollIndicator = NO;
// 宽高伸缩,高度固定死为一个popMenuItem高度
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_scrollView.frame = CGRectMake(0, 0, self.frame.size.width, kBottomMenuItemH);
_scrollView.backgroundColor = [UIColor whiteColor];
[_contentView addSubview:_scrollView];
} #pragma mark - 父类接口方法
// 本父类方法的作用:控制popMenuItem的状态切换,假设popMenuItem有子标题(如美食),显示子标题showSubTitleImgView,假设没有子标题(如电影),就可隐藏掉弹出button了
- (void)popMenuItemClicked:(PopMenuItem *)item
{
// 父类提供的一个接口方法,当它子类中的MenuItem addTarget为 popMenuItemClicked时,假设子类 没有实现popMenuItemClicked方法,就会到父类这儿来找popMenuItemClicked方法,
// 因此,本方法的目的是:监听全部它的子类(如CategoryPopMenu,DistrictPopMenu)的菜单项的点击 // 1.控制item的状态切换
_selectedPopMenuItem.selected = NO;
item.selected = YES;
_selectedPopMenuItem = item; // 2.查看是菜单项,如美食,如海淀区 否有子分类,假设有子分类才要显示SubTitleImgView,并为其提供数据源,子标题文字组成的数组
if (item.subTitlesArr.count) {
// 有子标题,才要动画显示全部的子标题
[self showSubTitleImgView:item];
} else { // 由于没有子标题,所以隐藏全部的子标题,而且就能够直接设置当前Category或District或Order为刚才点击的PopMenuItem
[self hideSubTitleImgView:item];
}
} // 假设被点击的popMenuItem有子标题,才要创建而且动画显示SubTitleImgView,并为其提供数据源,即子标题文字组成的数组
- (void)showSubTitleImgView:(PopMenuItem *)item
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kDefaultAnimDuration];
// 全部的PopMenu子类(如CategoryPopMenu和DistrictPopMenu和OrderPopMenu)共用一个_subTitleImgView,展示子分类的全部子标题
if (_subTitleImgView == nil) {
_subTitleImgView = [[SubTitleImgView alloc] init];
// 设置self PopMenu为_subTitleImgView的代理目的是:两个,当点击_subTitleImgView里面的button时,获知button的标题,另外一个就是告诉_subTitleImgView当前选中的PopMenu是哪一个类别,是美食?还是海淀区???
// 而且代理方法,由对应的子类(如CategoryPopMenu和DistrictPopMenu和OrderPopMenu)去实现
_subTitleImgView.delegate = self;
} // 设置子标题的frame,y位于scrollView的下方,高度? ??? ?
_subTitleImgView.frame = CGRectMake(0, kBottomMenuItemH, self.frame.size.width, _subTitleImgView.frame.size.height);
// 设置子标题的主标题 ?? ??
_subTitleImgView.mainTitle = [item titleForState:UIControlStateNormal];
// 设置子标题须要显示的内容(带去要显示的数据源,即全部的子标题组成的数组)
// 重要~~~供子类 去实现的,内部会拦截此方法,加入全部的子标题button,并设置文字
_subTitleImgView.titleArr = item.subTitlesArr; // 当前子标题没有正在展示的时候,就须要运行动画显示 _subTitleImgView
if (_subTitleImgView.superview == nil) {
[_subTitleImgView showSubTitleImgViewWithAnimation];
} // 加入子标题到内容view-scrollView底部
[_contentView insertSubview:_subTitleImgView belowSubview:_scrollView]; // 重要~依据_subTitleImgView不同的高度,调整整个contentView的高度~~~
CGRect cf = _contentView.frame;
cf.size.height = CGRectGetMaxY(_subTitleImgView.frame);
_contentView.frame = cf; [UIView commitAnimations];
} // 由于假设被点击的popMenuItem没有子标题,所以隐藏全部的子标题,而且就能够直接设置当前Category或District或Order为刚才点击的PopMenuItem
- (void)hideSubTitleImgView:(PopMenuItem *)item
{
// 1.通过动画隐藏子标题
if (_subTitleImgView) {
[_subTitleImgView hideSubTitleImgViewWithAnimation];
} // 2.移除后,必须又一次调整contentView的高度为默认的一个PopMenuItem的高度
CGRect cf = _contentView.frame;
cf.size.height = kBottomMenuItemH;
_contentView.frame = cf; // 3.由于没有子标题,所以就能够直接设置工具类中的当前Category或District或Order为刚才点击的PopMenuItem,工具类内部会拦截,并发出通知,通知给TopMenu等
NSString *title = [item titleForState:UIControlStateNormal];
if ([item isKindOfClass:[CategoryPopMenuItem class]]) {
// 假设点击的PopMenuItem是 分类PopMenuItem
[MetaDataTool sharedMetaDataTool].currentCategoryName = title;
} else if ([item isKindOfClass:[DistrictPopMenuItem class]]) {
// 假设点击的PopMenuItem是 商区PopMenuItem
[MetaDataTool sharedMetaDataTool].currentDistrictName = title;
} else {
// 假设点击的PopMenuItem是 排序PopMenuItem
[MetaDataTool sharedMetaDataTool].currentOrder = [[MetaDataTool sharedMetaDataTool] orderWithName:title];
}
} #pragma mark 显示ContentView,供外部调用,如点击了TopMenu时调用,且当前没有PopMenu正在显示
- (void)showPopMenu
{
_contentView.transform = CGAffineTransformMakeTranslation(0, -_contentView.frame.size.height);
_contentView.alpha = 0;
_cover.alpha = 0;
[UIView animateWithDuration:kDefaultAnimDuration animations:^{
// 1.scrollView从上面 -> 以下
_contentView.transform = CGAffineTransformIdentity;
_contentView.alpha = 1; // 2.遮盖(0 -> 0.4)
[_cover alphaReset];
}];
}
#pragma mark 隐藏ContentView,供外部调用,如点击了Cover或同一个TopMenuItem时调用,且当前没有PopMenu正在显示
// 如点击遮盖时,隐藏并移除popMenu的contentView(内部是scrollView和subTitleImgView),并置cover透明
- (void)hidePopMenu
{
// 假设隐藏完成弹出菜单的 _contentView之后,要通知调用者(顶部菜单)更改顶部菜单项文字
if (_hideBlock) {
_hideBlock();
}
[UIView animateWithDuration:kDefaultAnimDuration animations:^{
// _contentView向上消失,即移动一个自身的高度
_contentView.transform = CGAffineTransformMakeTranslation(0, -_contentView.frame.size.height);
_contentView.alpha = 0; // 2.遮盖(0.4 -> 0)
_cover.alpha = 0;
} completion:^(BOOL finished) {
// _contentView全然看不见了之后,就将弹出菜单从父控件中移除
[self removeFromSuperview]; // 而且恢复_contentView的属性
_contentView.transform = CGAffineTransformIdentity;
_contentView.alpha = 1;
[_cover alphaReset];
}];
} @end

PopMenuItem.h

//
// PopMenuItem.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 底部弹出菜单的菜单项 (是一个父类) 抽取的特征:1,右边有分隔线,2.宽高全统一,3.选中时,背景图片统一,4文字颜色 #import <UIKit/UIKit.h> @interface PopMenuItem : UIButton // 本接口,专门交给子类实现
// 数据源,子标题数组,全部子标题的名字组成的数组,
// 比方 美食 以下有多少个category
// 比方 海淀区 以下有多少个place
- (NSArray *)subTitlesArr;
@end

PopMenuItem.m

//
// PopMenuItem.m
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 底部弹出菜单的菜单项 (是一个父类) 抽取的特征:1,右边有分隔线,2.宽高全统一,3.选中时,背景图片统一,4文字颜色 #import "PopMenuItem.h" @implementation PopMenuItem - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1.右边的切割线
UIImage *img = [UIImage imageNamed:@"separator_filter_item.png"];
UIImageView *divider = [[UIImageView alloc] initWithImage:img];
divider.bounds = CGRectMake(0, 0, 2, kBottomMenuItemH * 0.7);
divider.center = CGPointMake(kBottomMenuItemW, kBottomMenuItemH * 0.5);
[self addSubview:divider]; // 2.文字颜色
[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.titleLabel.font = [UIFont systemFontOfSize:16]; // 3.设置被选中时的背景
[self setBackgroundImage:[UIImage imageStretchedWithName:@"bg_filter_toggle_hl.png"] forState:UIControlStateSelected];
}
return self;
} // 菜单项的宽高固定为一个button的宽和高
- (void)setFrame:(CGRect)frame
{
frame.size = CGSizeMake(kBottomMenuItemW, kBottomMenuItemH);
[super setFrame:frame];
} // 取消高亮显示状态
- (void)setHighlighted:(BOOL)highlighted {} // 本接口,专门交给子类实现
// 数据源,子标题数组,全部子标题的名字组成的数组
// 比方 美食 以下有多少个category
// 比方 海淀区 以下有多少个place
- (NSArray *)subTitlesArr
{
return nil;
} @end

Cover.h

//
// Cover.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// PopMenu是点击顶部button项(TopMenuItem),在其下方弹出的菜单(XXXPopMenu)的父类,成员有:以下是一个cover蒙板,上面是一个contentView(包括着scrollView和subTitleImgView,当中scrollView里面放的全是PopMenuItem,如美食,如海淀区.....subTitleImgView里面放的全是美食以下的全部子标题,如川菜湘菜粤菜) #import <UIKit/UIKit.h> @interface Cover : UIView + (id)cover;
// 绑定tap手势
+ (id)coverWithTarget:(id)target action:(SEL)action; - (void)alphaReset;
@end

Cover.m

//
// Cover.m
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// PopMenu是点击顶部button项(TopMenuItem),在其下方弹出的菜单(XXXPopMenu)的父类,成员有:以下是一个cover蒙板,上面是一个contentView(包括着scrollView和subTitleImgView,当中scrollView里面放的全是PopMenuItem,如美食,如海淀区.....subTitleImgView里面放的全是美食以下的全部子标题,如川菜湘菜粤菜) #import "Cover.h" // 1为全黑
#define kAlpha 0.7
@implementation Cover - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1.背景色
self.backgroundColor = [UIColor blackColor]; // 2.它是蒙在tableView上面,所以要同tableView一样,宽高自己主动伸缩
self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; // 3.透明度
self.alpha = kAlpha;
}
return self;
} - (void)alphaReset
{
self.alpha = kAlpha;
} + (id)cover
{
return [[self alloc] init];
} + (id)coverWithTarget:(id)target action:(SEL)action
{
Cover *cover = [self cover];
// 绑定一个,tap手势监听器
[cover addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:target action:action]];
return cover;
} @end

SubTitleImgView.h

容纳全部子标题的ImgView,里面全是一个个button,

如美食以下的川菜、湘菜、粤菜等...

如海淀区以下的中关村、五棵松、香山等...

而且全部的弹出菜单PopMenu都共用此一个SubTitleImgView

//
// SubTitleImgView.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 容纳全部子标题的ImgView,里面全是一个个button,如美食以下的川菜、湘菜、粤菜等...如海淀区以下的中关村、五棵松、香山等,注意全部的弹出菜单PopMenu共用此一个SubTitleImgView #import <UIKit/UIKit.h> @protocol SubTitleImgViewDelegate; @interface SubTitleImgView : UIImageView // 数据源,主标题,每个子标题数组都有,且是在第一个位置---> 【全部】
@property (nonatomic, copy) NSString *mainTitle; // 数据源,须要显示的全部的子标题button的文字组成的数组,外部传入,如美食以下的川菜、湘菜、粤菜等...如海淀区以下的中关村、五棵松、香山等
@property (nonatomic, strong) NSMutableArray *subTitlesArr; @property (nonatomic, weak) id<SubTitleImgViewDelegate> delegate;
// 代理和block的效果等价
//@property (nonatomic, copy) void (^setBtnTitleBlock)(NSString *title);
//@property (nonatomic, copy) NSString *(^getBtnTitleBlock)(); // 通过动画显示出来SubTitleImgView,供创建者调用
- (void)showSubTitleImgViewWithAnimation;
// 通过动画隐藏SubTitleImgView,供创建者调用
- (void)hideSubTitleImgViewWithAnimation;
@end

SubTitleImgView.h

容纳全部子标题的ImgView,里面全是一个个button,

如美食以下的川菜、湘菜、粤菜等...

如海淀区以下的中关村、五棵松、香山等...

而且全部的弹出菜单PopMenu都共用此一个SubTitleImgView

//
// SubTitleImgView.m
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 容纳全部子标题的ImgView,里面全是一个个button,如美食以下的川菜、湘菜、粤菜等...如海淀区以下的中关村、五棵松、香山等,注意全部的弹出菜单PopMenu共用此一个SubTitleImgView #import "SubTitleImgView.h"
#import "MetaDataTool.h"
#import "SubTitleImgViewDelegate.h" #define kSubTitleBtnW 100
#define kSubTitleBtnH 40 // 里面用到的全部button,因样式统一,所以抽取一个基类
@interface SubTitleBtn : UIButton
@end @implementation SubTitleBtn
- (void)drawRect:(CGRect)rect
{
// 设置选中状态下,SubTitleBtn的frame和背景
if (self.selected) {
CGRect frame = self.titleLabel.frame;
frame.origin.x -= 5;
frame.size.width += 10;
frame.origin.y -= 5;
frame.size.height += 10;
[[UIImage imageStretchedWithName:@"slider_filter_bg_active.png"] drawInRect:frame];
}
}
@end @interface SubTitleImgView()
{
// 记住当前选中的SubTitleBtn
UIButton *_selectedSubTitleBtn;
}
@end @implementation SubTitleImgView - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// SubTitleImgView的宽度自由伸缩
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
// SubTitleImgView的背景图片
self.image = [UIImage imageStretchedWithName:@"bg_subfilter_other.png"]; // 重要~~~~裁剪掉超出父控件范围内的子控件(超出父控件范围内的子控件不显示)
self.clipsToBounds = YES;
// 让imageView里面的一个个button能够点击,如美食(PopMenuItem)以下的川菜、湘菜、粤菜等
self.userInteractionEnabled = YES;
}
return self;
}
#pragma mark - 拦截setter数据源数组方法,创建全部相应个数的button
// 数据源,需要显示的全部的子标题button的文字组成的数组,外部传入,拦截setter方法
- (void)setTitleArr:(NSArray *)titleArr
{
// 1.用成员变量,记住全部的子标题,如美食(PopMenuItem)以下的川菜、湘菜、粤菜等
// 全部子标题中,排在首位的都是:固定字符串--->【全部】
[_subTitlesArr addObject:kAll];
// 将其它子标题加到后面,如美食(PopMenuItem)以下的川菜、湘菜、粤菜等
[_subTitlesArr addObjectsFromArray:titleArr]; // 2.遍历子标题数组,懒载入创建可重用的子标题button
[self addAllSubTitlesBtn]; // 3.每当setter数据源改变之后,button的位置和个数都要又一次排布,所以手动调用一次 layoutSubviews方法
[self layoutSubviews]; /*
layoutSubviews在以下情况下会被调用:
1、init初始化不会触发layoutSubviews
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
*/
} // 2.遍历子标题数组,懒载入创建可重用的子标题button
- (void)addAllSubTitlesBtn
{ int count = _subTitlesArr.count;
// 遍历子标题数组,懒载入创建button,并设置button的文字
for (int i = 0; i<count; i++) {
// 1.重要~~~如i位置没有button与之相应,则创建一个新的button,显示i位置的子标题文字
UIButton *btn = nil;
// self是SubTitleImgView,里面的子控件全是一个个代表子标题的button
// button个数不够,创建SubTitleBtn,而且绑定监听事件
if (i >= self.subviews.count) {
// 创建一个新的子标题button
btn = [SubTitleBtn buttonWithType:UIButtonTypeCustom];
// 绑定监听事件
[btn addTarget:self action:@selector(subTitleBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
// 设置文字颜色
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 设置文字字体
btn.titleLabel.font = [UIFont systemFontOfSize:13];
// 加入到SubTitleImgView
[self addSubview:btn];
} else {
// 假设子控件数组中有足量的button,就直接取出来,重用
btn = self.subviews[i];
} // 2.设置button独一无二的文字(并将button显示)
[btn setTitle:_subTitlesArr[i] forState:UIControlStateNormal];
btn.hidden = NO; // 3.推断该button要不要默认选中,依据是:该button文字是不是和当前选中的分类或商区名字一样,代理会负责告诉我subTitleImgView 当前的分类名或者商区名字
if ([_delegate respondsToSelector:@selector(subTitleImgViewGetCurrentBtnTitle:)]) {
// 代理会负责告诉我subTitleImgView 当前的分类名或者商区名字
NSString *currentBtnTitle = [_delegate subTitleImgViewGetCurrentBtnTitle:self]; // 选中了主标题,选中第0个button(“全部”)
if ([currentBtnTitle isEqualToString:_mainTitle] && i == 0) {
btn.selected = YES;
_selectedSubTitleBtn = btn;
} else {
btn.selected = [_subTitlesArr[i] isEqualToString:currentBtnTitle];
// 重要细节 ~~~~假设在不同的类别或商区,发现了同名的,则也视为选中了
if (btn.selected) {
_selectedSubTitleBtn = btn;
}
}
} else {
btn.selected = NO;
}
} // 3.重要~~~隐藏子控件数组中多余的button,如子标题文字数组有8项,而子控件数组有10个,那么多余的两个button就要隐藏起来
for (int i = count; i<self.subviews.count; i++) {
UIButton *btn = self.subviews[i];
btn.hidden = YES;
}
}
// 监听小标题(button)点击,如美食(PopMenuItem)以下的川菜、湘菜、粤菜等
- (void)subTitleBtnClicked:(UIButton *)btn
{
// 1.三步曲,控制button状态切换
_selectedSubTitleBtn.selected = NO;
btn.selected = YES;
_selectedSubTitleBtn = btn; // 2.告诉代理,我点击选中了谁
if ([_delegate respondsToSelector:@selector(subTitleImgView:btnClicked:)]) { // 取出当前被点的button的文字
NSString *title = [btn titleForState:UIControlStateNormal];
// 假设 是 【全部】
if ([title isEqualToString:kAll]) { // 全部 --> 大标题
title = _mainTitle;
}
// 告诉代理(调用者),当前被点击的button的文字...
[_delegate subTitleImgView:self btnClicked:title];
}
} #pragma mark - 覆盖UIView的方法,又一次布局SubTitleImgView全部子控件的位置
// 控件SubTitleImgView本身的宽高发生改变等情况下就会自己主动触发layoutSubviews方法
- (void)layoutSubviews
{
// 1.一定要调用super
[super layoutSubviews]; // 2.依据屏幕宽,算出总的列数,并对全部子标题button设置九宫格frame
int columns = self.frame.size.width / kSubTitleBtnW;
// 依据数据源的个数,遍历相应数目的button,依据i设置其frame
for (int i = 0; i<_subTitlesArr.count; i++) { UIButton *btn = self.subviews[i];
// 设置位置
// 所在的列
CGFloat x = i % columns * kSubTitleBtnW;
// 所在的行
CGFloat y = i / columns * kSubTitleBtnH;
// 设置独一无二的frame
btn.frame = CGRectMake(x, y, kSubTitleBtnW, kSubTitleBtnH);
} // 3.重要~~~计算出子标题的行数以后,必需要设置SubTitleImgView的总高度,三步曲
// 小算法,求出总的行数,以确定SubTitleImgView的总高度
int rows = (_subTitlesArr.count + columns - 1) / columns;
CGRect frame = self.frame;
frame.size.height = rows * kSubTitleBtnH;
self.frame = frame;
} #pragma mark - 显示和隐藏子标题ImgView,供创建者调用
// 动画显示self (SubTitleImgView),供创建者调用
- (void)showSubTitleImgViewWithAnimation
{
// 1.重要~~~必需要先调用layoutSubviews,先算出在当前数据源titleArr数组个数的情况下,self的总高度~~~~
[self layoutSubviews]; // 2.先设置y为负的self的总高度(方法ayoutSubviewsy已经计算过了)
self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height);
// 先设置为透明
self.alpha = 0; // 3.动画显示出来
[UIView animateWithDuration:kDefaultAnimDuration animations:^{
self.transform = CGAffineTransformIdentity;
self.alpha = 1;
}];
}
// 动画隐藏self (SubTitleImgView),供创建者调用
- (void)hideSubTitleImgViewWithAnimation
{
// 动画设置y为负的self的总高度(慢慢向上消失效果)
[UIView animateWithDuration:kDefaultAnimDuration animations:^{
self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height);
self.alpha = 0;
} completion:^(BOOL finished) {
// 重要~~~~动画完毕后,将其从父控件中移除,而且将self的高度置0,目的是方便下次动画出现的时候,能够从0開始向下展开
[self removeFromSuperview]; CGRect f = self.frame;
f.size.height = 0;
self.frame = f;
// ? ?? 会不会与上面这一句功能反复
self.transform = CGAffineTransformIdentity;
self.alpha = 1;
}];
}
@end

SubTitleImgView定义的协议

SubTitleImgViewDelegate.h

//
// SubTitleViewDelegate.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 分类或商区的子标题的代理方法,当点击了【分类或商区的子标题button】时,通知代理 #import <Foundation/Foundation.h> @class SubTitleImgView;
@protocol SubTitleImgViewDelegate <NSObject> @optional // 当SubTitleImgView里面的button被点击了的时候调用,告诉其它全部想知道的类(即SubTitleImgView的代理):被点击的button的文字【被点击的分类或商区的子标题button上的文字】
- (void)subTitleImgView:(SubTitleImgView *)subTitleImgView btnClicked:(NSString *)btnTitle; // 返回当前选中的文字(比方分类菜单。就返回当前选中的分类名称。区域菜单。就返回当前选中的区域名称),目的是子标题button出现前,将选中的那个高亮(回显)~~~
// 得到当前选中的分类或商区button上的文字,用于与新出现的button文字进行推断,假设同样,则在SubTitleImgView出现之前,将SubTitleImgView上面的该button置为高亮,其它全为普通
// 假设SubTileImgView的代理是CategoryPopMenu,说明应该从工具类返回currentCategoryName给SubTileImgView
- (NSString *)subTitleImgViewGetCurrentBtnTitle:(SubTitleImgView *)subTitleImgView;
@end

View的层级关系示意图:

父类:PopMenu

其子类:CategoryPopMenu、DistrictPopMenu、OrderPopMenu



父类:PopMenuItem

其子类:CategoryPopMenuItem、DistrictPopMenuItem、OrderPopMenuItem

子类:CategoryPopMenu

//
// CategoryPopMenu.h
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击顶部菜单中的分类频道 button(顶部菜单项),弹出的菜单,继承自PopMenu,包含二个部分(上:contentView,包含scrollView和SubTitleImgView,下:蒙板) #import "PopMenu.h" @interface CategoryPopMenu : PopMenu @end
//
// CategoryPopMenu.m
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击顶部菜单中的分类频道 button(顶部菜单项),弹出的菜单,继承自PopMenu,包含二个部分(上:contentView,包含scrollView和SubTitleImgView,下:蒙板) #import "CategoryPopMenu.h"
// 分类菜单项如:美食
#import "CategoryPopMenuItem.h"
#import "MetaDataTool.h" @implementation CategoryPopMenu - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1.往scrollView里面加入内容(CategoryPopMenuItem)
[self addCategoryPopMenuItem];
}
return self;
}
// 1.往scrollView里面加入内容(CategoryPopMenuItem)
- (void)addCategoryPopMenuItem
{
// 获取数据源,工具类提供allCategoriesArr对象数组
NSArray *categories = [MetaDataTool sharedMetaDataTool].allCategoriesArr; // 1.往scrollView里面加入内容(CategoryPopMenuItem)
int count = categories.count;
for (int i = 0; i<count; i++) {
// 创建并逐一加入CategoryPopMenuItem, 分类菜单项如:美食
CategoryPopMenuItem *item = [[CategoryPopMenuItem alloc] init];
// 设置CategoryPopMenuItem要显示的数据源
item.category = categories[i];
// 当点击美食时,会到父类中去找方法popMenuItemClicked
[item addTarget:self action:@selector(popMenuItemClicked:) forControlEvents:UIControlEventTouchUpInside];
// 由于是在scrollView里面,所以所有水平排列
item.frame = CGRectMake(i * kBottomMenuItemW, 0, 0, 0);
// 加到scrollView
[_scrollView addSubview:item]; // 默认选中第0个item
if (i == 0) {
item.selected = YES;
_selectedPopMenuItem = item;
}
}
// 2.依据CategoryPopMenuItem的多少,设置_scrollView滚动范围
_scrollView.contentSize = CGSizeMake(count * kBottomMenuItemW, 0);
} #pragma mark - SubTitleImgViewDelegate代理方法
// 当SubTitleImgView里面的子标题button点击时,会调用此方法,目的是 传递点击的【分类或商区的子标题button】文字
- (void)subTitleImgView:(SubTitleImgView *)subTitleImgView btnClicked:(NSString *)btnTitle
{
[MetaDataTool sharedMetaDataTool].currentCategoryName = btnTitle;
}
// 难点??? 得到并推断当前button是否选中的文字(比方分类菜单,就返回当前选中的分类名称;区域菜单,就返回当前选中的区域名称)
- (NSString *)subTitleImgViewGetCurrentBtnTitle:(SubTitleImgView *)subTitleImgView
{
// 假设SubTileImgView的代理是CategoryPopMenu,说明应该从工具类返回currentCategoryName给SubTileImgView
return [MetaDataTool sharedMetaDataTool].currentCategoryName;
}
@end

子类:CategoryPopMenuItem

//
// CategoryPopMenuItem.h
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 在CategoryPopMenu的第一层(即scrollView)里面的一个button,如美食,button图片在上面,文字在以下,且button右边是一根竖线 #import "PopMenuItem.h"
@class MyCategory;
@interface CategoryPopMenuItem : PopMenuItem // 数据源,本button,须要显示的分类对象模型,一个PopMenuItem 相应一个分类,如美食
@property (nonatomic, strong) MyCategory *category;
@end
//
// CategoryPopMenuItem.m
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 在CategoryPopMenu的第一层(即scrollView)里面的一个button,button图片在上面,文字在以下,且button右边是一根竖线 #import "CategoryPopMenuItem.h"
#import "MyCategory.h" // 图片在上,文字在下
#define kTitleHeightRatio 0.3
@implementation CategoryPopMenuItem - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1.文字居中对齐
self.titleLabel.textAlignment = NSTextAlignmentCenter; // 2.图片中心缩放
self.imageView.contentMode = UIViewContentModeCenter;
}
return self;
} // 父类的方法,供子类实现
// 数据源,子标题数组,全部子标题的名字组成的数组, 本接口,专门交给子类实现
// 比方 美食 以下有多少个subCategory
// 比方 海淀区 以下有多少个place
- (NSArray *)subTitlesArr
{
return _category.subcategories;
} // 拦截数据源的setter方法,设置button的图片和文字
- (void)setCategory:(MyCategory *)category
{
_category = category; // 1.图标
[self setImage:[UIImage imageNamed:category.icon] forState:UIControlStateNormal]; // 2.标题
[self setTitle:category.name forState:UIControlStateNormal];
} #pragma mark 设置button上面的图片的frame
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
return CGRectMake(0, 0, contentRect.size.width, contentRect.size.height * (1 - kTitleHeightRatio));
} #pragma mark 设置button以下的标题的frame
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
CGFloat titleHeight = contentRect.size.height * kTitleHeightRatio;
CGFloat titleY = contentRect.size.height - titleHeight;
return CGRectMake(0, titleY, contentRect.size.width, titleHeight);
} @end

子类:DistrictPopMenu

//
// DistrictPopMenu.h
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击顶部菜单中的 所有商区 button(顶部菜单项),弹出的菜单,继承自PopMenu,包含二个部分(上:contentView,包含scrollView和SubTitleImgView,下:蒙板) #import "PopMenu.h" @interface DistrictPopMenu : PopMenu @end
//
// DistrictPopMenu.m
// 帅哥_团购
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 点击顶部菜单中的 所有商区 button(顶部菜单项),弹出的菜单,继承自PopMenu,包含二个部分(上:contentView,包含scrollView和SubTitleImgView,下:蒙板) #import "DistrictPopMenu.h"
#import "DistrictPopMenuItem.h"
#import "MetaDataTool.h"
#import "District.h"
// 商区依赖城市
#import "City.h" #import "SubTitleImgView.h" @interface DistrictPopMenu ()
{
NSMutableArray *_menuItems;
} @end
@implementation DistrictPopMenu - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_menuItems = [NSMutableArray array]; [self cityChange]; // 监听城市改变
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cityChange) name:kCityChangeNote object:nil];
}
return self;
} - (void)cityChange
{
// 1.获取当前选中的城市对象,保存在工具中
City *city = [MetaDataTool sharedMetaDataTool].currentCity; // 2.当前城市的所有区域,包含所有商区+下属商区数组(城市相应的成员)
NSMutableArray *districts = [NSMutableArray array];
// 2.1.所有商区
District *all = [[District alloc] init];
all.name = kAllDistrict;
[districts addObject:all];
// 2.2.其它商区,下属商区数组(城市相应的成员)
[districts addObjectsFromArray:city.districts]; // 3.遍历所有的商区对象,创建并设置button标题
int count = districts.count;
for (int i = 0; i<count; i++) {
DistrictPopMenuItem *item = nil;
if (i >= _menuItems.count) { // 不够
item = [[DistrictPopMenuItem alloc] init];
[item addTarget:self action:@selector(popMenuItemClicked:) forControlEvents:UIControlEventTouchUpInside];
[_menuItems addObject:item];
[_scrollView addSubview:item];
} else {
item = _menuItems[i];
} item.hidden = NO;
item.district = districts[i];
item.frame = CGRectMake(i * kBottomMenuItemW, 0, 0, 0); // 默认选中第0个item
if (i == 0) {
item.selected = YES;
_selectedPopMenuItem = item;
} else {
item.selected = NO;
}
} // 4.隐藏多余的item
for (int i = count; i<_menuItems.count; i++) {
DistrictPopMenuItem *item = _scrollView.subviews[i];
item.hidden = YES;
} // 5.设置scrollView的内容尺寸
_scrollView.contentSize = CGSizeMake(count * kBottomMenuItemW, 0); // 6.隐藏子标题(在父类定义的)
[_subTitleImgView hideSubTitleImgViewWithAnimation];
} #pragma mark - SubTitleImgViewDelegate代理方法
// 当SubTitleImgView里面的子标题button点击时,会调用此方法,目的是 传递点击的【分类或商区的子标题button】文字
- (void)subTitleImgView:(SubTitleImgView *)subTitleImgView btnClicked:(NSString *)btnTitle
{
[MetaDataTool sharedMetaDataTool].currentDistrictName = btnTitle;
}
// 难点?? ? 得到并推断当前button是否选中的文字(比方分类菜单,就返回当前选中的分类名称。区域菜单,就返回当前选中的区域名称)
- (NSString *)subTitleImgViewGetCurrentBtnTitle:(SubTitleImgView *)subTitleImgView
{
// 假设SubTileImgView的代理是DistrictPopMenu,说明应该从工具类返回currentDistrictName给SubTileImgView
return [MetaDataTool sharedMetaDataTool].currentDistrictName;
} // 顶部菜单由于要改变其三个button的文字,因此在通知中心注冊成为了监听者,因此dealloc时要在通知中心,移除掉监听者
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

子类:DistrictPopMenuItem

//
// DistrictPopMenuItem.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 如海淀区 #import "PopMenuItem.h"
@class District;
@interface DistrictPopMenuItem : PopMenuItem // 数据源 一个PopMenuItem相应一个商区,如海淀区
@property (nonatomic, strong) District *district;
@end
//
// DistrictPopMenuItem.m
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 如海淀区 #import "DistrictPopMenuItem.h"
#import "District.h"
@implementation DistrictPopMenuItem - (void)setDistrict:(District *)district
{
_district = district; [self setTitle:district.name forState:UIControlStateNormal];
} // 父类的方法,供子类实现
// 数据源,子标题数组,全部子标题的名字组成的数组, 本接口,专门交给子类实现
// 比方 美食 以下有多少个subCategory
// 比方 海淀区 以下有多少个place
- (NSArray *)subTitlesArr
{
return _district.places;
} @end

子类:OrderPopMenu

//
// OrderPopMenu.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// #import "PopMenu.h" @interface OrderPopMenu : PopMenu @end
//
// OrderPopMenu.m
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// #import "OrderPopMenu.h"
#import "OrderPopMenuItem.h"
#import "Order.h"
#import "MetaDataTool.h"
@implementation OrderPopMenu - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1.往UIScrollView加入内容
NSArray *orders = [MetaDataTool sharedMetaDataTool].AllOrdersArr;
int count = orders.count; for (int i = 0; i<count; i++) {
// 创建排序item
OrderPopMenuItem *item = [[OrderPopMenuItem alloc] init];
item.order = orders[i];
[item addTarget:self action:@selector(popMenuItemClicked:) forControlEvents:UIControlEventTouchUpInside];
item.frame = CGRectMake(i * kBottomMenuItemW, 0, 0, 0);
[_scrollView addSubview:item]; // 默认选中第0个item
if (i == 0) {
item.selected = YES;
_selectedPopMenuItem = item;
}
}
_scrollView.contentSize = CGSizeMake(count * kBottomMenuItemW, 0);
}
return self;
}
@end

子类:OrderPopMenuItem

//
// OrderPopMenuItem.h
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// #import "PopMenuItem.h"
@class Order;
@interface OrderPopMenuItem : PopMenuItem @property (nonatomic, strong) Order *order;
@end
//
// OrderPopMenuItem.m
// 帅哥_团购
//
// Created by beyond on 14-8-16.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// #import "OrderPopMenuItem.h"
#import "Order.h"
@implementation OrderPopMenuItem - (void)setOrder:(Order *)order
{
_order = order; [self setTitle:order.name forState:UIControlStateNormal];
}
@end

最重要的一个工具类

//
// MetaDataTool.h
// 帅哥_团购
//
// Created by beyond on 14-8-14.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 元数据管理类
// 1.城市数据
// 2.下属分区数据
// 3.分类数据 #import <Foundation/Foundation.h>
@class Order;
@class City;
@interface MetaDataTool : NSObject
singleton_interface(MetaDataTool) // readonly仅仅可读,NSArray,不同意外部随便增删改
// 全部的城市分组数组,数组中的元素是section对象
@property (nonatomic, strong, readonly) NSMutableArray *allSectionsArr; // 全部城市字典,Key是城市名,Value是城市对象
@property (nonatomic, strong, readonly) NSMutableDictionary *allCitiesDict; // 当前选中的城市, 当点击了控制器下方的tableView的某一行时,会设置当前城市,拦截setter操作,更新近期訪问的城市数组
@property (nonatomic, strong) City *currentCity; // 当前选中的城市 // 全部的分类对象组成的数组,一个分类对象包含分类名,图标,全部子分类名组成的数组
@property (nonatomic, strong, readonly) NSArray *allCategoriesArr; // 全部的排序对象组成的数组
@property (nonatomic, strong, readonly) NSArray *AllOrdersArr; @property (nonatomic, strong) NSString *currentCategoryName; // 当前选中的类别的名字
@property (nonatomic, strong) NSString *currentDistrictName; // 当前选中的区域名字
@property (nonatomic, strong) Order *currentOrder; // 当前选中的排序对象 // 通过button上面的名字如(价格最高),到MyOrder对象数组中,遍历返回MyOder对象
- (Order *)orderWithName:(NSString *)name; @end

//
// MetaDataTool.m
// 帅哥_团购
//
// Created by beyond on 14-8-14.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 元数据管理类
// 1.城市数据
// 2.下属分区数据
// 3.分类数据 #import "MetaDataTool.h"
// 一个分组模型
#import "Section.h"
#import "City.h" // 一个分类对象模型
#import "MyCategory.h"
#import "Order.h"
// 沙盒里面放的是全部以前訪问过的城市名字
#define kFilePath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"visitedCityNamesArr.data"]
@interface MetaDataTool ()
{
// 数组,存储以前訪问过城市的名称
NSMutableArray *_visitedCityNamesArr;
// 訪问过的组section
Section *_visitedSection; // 近期訪问的城市组数组
} @end @implementation MetaDataTool
singleton_implementation(MetaDataTool) - (id)init
{
if (self = [super init]) {
// 初始化项目中的全部元数据 // 1.初始化城市数据 [self loadCitiesData]; // 2.初始化分类数据
[self loadCategoryData]; // 3.初始化排序对象数据
[self loadOrderData];
}
return self;
} // 1.初始化城市数据
- (void)loadCitiesData
{
// 全部城市对象组成的字典,Key是城市名,Value是城市对象
_allCitiesDict = [NSMutableDictionary dictionary];
// 暂时变量,存放全部的section
NSMutableArray *tempSectionsArr = [NSMutableArray array]; // 1.创建一个热门城市分组
Section *hotSection = [[Section alloc] init];
// 组名是 热门
hotSection.name = @"热门";
// 分组的成员cities,初始化
hotSection.cities = [NSMutableArray array];
// 为了将热门这一组加在分组数组的最前面,准备了一个暂时的section数组
[tempSectionsArr addObject:hotSection]; // 2.加入A-Z分组,到暂时section数组后面
// 载入plist数据
NSArray *sectionsArr = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Cities.plist" ofType:nil]];
for (NSDictionary *dict in sectionsArr) {
// 创建城市分组对象模型
Section *section = [[Section alloc] init];
// 调用分类方法,将字典转成模型
[section setValuesWithDict:dict];
// 将全部的section对象,加到暂时的section对象数组的后面
[tempSectionsArr addObject:section]; // 遍历每一组的全部城市,找出热门的加到hotSection里面
for (City *city in section.cities) {
if (city.hot) {
// 假设是热门城市
[hotSection.cities addObject:city];
}
// 而且将全部的城市对象,以城市名作为键,存入字典中
[_allCitiesDict setObject:city forKey:city.name];
}
} // 3.从沙盒中读取之前訪问过的城市名称
_visitedCityNamesArr = [NSKeyedUnarchiver unarchiveObjectWithFile:kFilePath];
// 假设是首次使用,则沙盒中返回的是空数组,须要懒载入,创建一个数组
if (_visitedCityNamesArr == nil) {
_visitedCityNamesArr = [NSMutableArray array];
} // 4.创建并加入一个section, 近期訪问城市组(section)
_visitedSection = [[Section alloc] init];
_visitedSection.name = @"近期訪问";
_visitedSection.cities = [NSMutableArray array]; // 5.遍历沙盒中取出来的城市名组成的数组,转成一个个城市对象
for (NSString *name in _visitedCityNamesArr) {
// 依据城市名,从对象字典中取出城市对象,并加入到近期訪问城市组(section)中的城市对象数组
City *city = _allCitiesDict[name];
[_visitedSection.cities addObject:city];
}
// 6.假设近期訪问城市组(section)中的城市对象数组中有城市,那么就能够将近期訪问组插入到sections最前面
if (_visitedSection.cities.count) {
[tempSectionsArr insertObject:_visitedSection atIndex:0];
} // 将全部的section组成的数组赋值给成员变量供调用者訪问
_allSectionsArr = tempSectionsArr;
}
// 当点击了控制器下方的tableView的某一行时,会设置当前城市,拦截setter操作,更新近期訪问的城市数组
- (void)setCurrentCity:(City *)currentCity
{
_currentCity = currentCity; // 改动当前选中的区域
// _currentDistrict = kAllDistrict; // 1.先从近期訪问的城市名数组中,移除该的城市名
[_visitedCityNamesArr removeObject:currentCity.name]; // 2.再将新的城市名插入到数组的最前面(近期訪问的在最前)
[_visitedCityNamesArr insertObject:currentCity.name atIndex:0]; // 3.同一时候,要将新的城市对象,放到_visitedSection的城市对象数组的最前面
[_visitedSection.cities removeObject:currentCity];
[_visitedSection.cities insertObject:currentCity atIndex:0]; // 4.归档近期訪问的城市名组成的数组,以便下次再解档
[NSKeyedArchiver archiveRootObject:_visitedCityNamesArr toFile:kFilePath]; // 5.每一次点击,拦截setter当前城市之后,都要发出通知,做什么用???
[[NSNotificationCenter defaultCenter] postNotificationName:kCityChangeNote object:nil]; // 6.当用点击了某一行,来到了这个setCurrentCity方法时,肯定是要在最前面,加入“近期訪问组”了
// 假设 allSectionsArr 已经有了 【近期訪问组】,则不用再加入了
if (![_allSectionsArr containsObject:_visitedSection]) {
[_allSectionsArr insertObject:_visitedSection atIndex:0];
}
} // 3.初始化排序对象数组
- (void)loadOrderData
{
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Orders.plist" ofType:nil]];
int count = array.count;
NSMutableArray *temp = [NSMutableArray array];
for (int i = 0; i<count; i++){
Order *o = [[Order alloc] init];
// 设置MyOrder对象的名字
o.name = array[i];
// 设置MyOrder对象的索引 1 2 3 4 ...
o.index = i + 1;
[temp addObject:o];
}
// 全部的排序对象组成的数组
_AllOrdersArr = temp;
}
// 点击PopMenuItem时,可通过button上面的名字如(价格最高),到MyOrder对象数组中,遍历返回MyOder对象,以达到为本工具类设置成员currentOrder的目的
- (Order *)orderWithName:(NSString *)name
{
for (Order *order in _AllOrdersArr) {
if ([name isEqualToString:order.name]) {
return order;
}
}
return nil;
} // 2.初始化分类数据
- (void)loadCategoryData
{
NSMutableArray *tempArr = [NSMutableArray array];
// 从plist返回的字典数组
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Categories.plist" ofType:nil]];
// 第1个对象是----名字叫:全部分类,无子分类,图标是:
MyCategory *all = [[MyCategory alloc] init];
all.name = kAllCategory;
all.icon = @"ic_filter_category_-1.png";
[tempArr addObject:all]; // 1.加入全部分类
// 遍历字典,将字典转成对象模型
for (NSDictionary *dict in array) {
MyCategory *c = [[MyCategory alloc] init];
// 分类方法
[c setValuesWithDict:dict];
[tempArr addObject:c];
} _allCategoriesArr = tempArr;
} #pragma mark - 当点击了PopMenuItem的不论什么子类时,都会来到这种方法
// 设置当前的分类或商区或排序,并发出通知,通知那些须要做出对应的显示改变的东东,比如TopMenu,比如DealListController发送对应的请求,显示对应的查询的内容
- (void)setCurrentCategoryName:(NSString *)currentCategoryName
{
_currentCategoryName = currentCategoryName;
// 发出通知
[[NSNotificationCenter defaultCenter] postNotificationName:kCategoryChangeNote object:nil];
} - (void)setCurrentDistrictName:(NSString *)currentDistrictName
{
_currentDistrictName = currentDistrictName;
// 发出通知
[[NSNotificationCenter defaultCenter] postNotificationName:kDistrictChangeNote object:nil];
} - (void)setCurrentOrder:(Order *)currentOrder
{
_currentOrder = currentOrder;
// 发出通知
[[NSNotificationCenter defaultCenter] postNotificationName:kOrderChangeNote object:nil];
} @end

版权声明:本文博客原创文章。博客,未经同意,不得转载。

iOS_21团购_顶部菜单和弹出菜单联动的更多相关文章

  1. iOS_21团购_发送请求【点评】数据

    结果表明,一个简单的请求: 用到的点评封装的类: 使用tableView简单展示: // // DealListController.m // 帅哥_团购 // // Created by beyon ...

  2. Android的Toolbar(含溢出菜单设置[弹出菜单的使用])的使用PopMenu的样式

    http://blog.csdn.net/yingtian648/article/details/52432438(转载) 1.在Toolbar.xml中设置弹出菜单的风格(app:popupThem ...

  3. iOS_21团购_地图功能

    终于效果图: 右下角的回到用户位置button: MapController控制器, 是主控制器左側dock上面的[地图]button相应的控制器, 继承自ShowDealDetailControll ...

  4. jq 右键菜单在弹出菜单前如果需要显示与否的判断相关操作

    菜单插件(ContextMenu)接收一个额外的参数对象来设置菜单项的样式和绑定鼠标事件. 菜单插件(ContextMenu)支持一下参数设置: bindings 包含id的对象:函数组. 当关联的菜 ...

  5. 解决Mui中popover 顶部弹出菜单弹出位置不准确以及无法收回的问题

    前言 最近公司的项目转向使用Hbuilder开发移动端项目,其中想要通过在顶部标题栏加入弹出菜单的方式,来定位长列表的位置,如图所示. 问题 Mui的功能貌似还不是很完善,在使用这个弹出菜单的时候,发 ...

  6. 关于MFC主菜单和右键弹出菜单

    一.主菜单.弹出菜单和右键菜单的概念: 主菜单是窗口顶部的菜单,一个窗口或对话框只能有一个主菜单,但是主菜单可以被更改(SetMenu()更改): 创建方式:CMenu::CreateMenu(voi ...

  7. Android 使用PopupWindow实现弹出菜单

    在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...

  8. Swing-JPopupMenu弹出菜单用法-入门

    弹出菜单是GUI程序中非常常见的一种控件.它通常由鼠标右击事件触发,比如在windows系统桌面上右击时,会弹出一个包含“刷新”.“属性”等菜单的弹出菜单.Swing中的弹出菜单是JPopupMenu ...

  9. SetWinEventHook 事件钩子(有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动等)good

    相信消息钩子大家听的比较多,消息钩子能够在应用程序处理系统消息之前将其截获,提前处理并可以决定是否继续将消息往下传送,有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动 ...

随机推荐

  1. JavaScript对象的创建

    原文 简书原文:https://www.jianshu.com/p/6cb1e7b7e379 大纲 前言 1.简单方式创建对象的方法 2.工厂模式创建对象 3.构造函数模式创建对象 4.原型模式创建对 ...

  2. Java链接Redis时出现 “ERR Client sent AUTH, but no password is set” 异常的原因及解决办法

    Java链接Redis时出现 "ERR Client sent AUTH, but no password is set" 异常的原因及解决办法 [错误提示] redis.clie ...

  3. Linux下kill进程脚本

    Linux下kill进程脚本 在Linux有时会遇到需要kill同一个程序的进程,然而这个程序有多个进程,一一列举很是繁琐,使用按名字检索,统一kill Perl脚本 使用方法 kill_all.pl ...

  4. 【33.20%】【LA 4320】【Ping pong】

    [Description] N (3 ≤ N ≤ 20000) ping pong players live along a west-east street(consider the street ...

  5. Qt 模仿QQ截图 动态吸附直线

    最近在学Qt.学东西怎么能不动手. 就写了些小程序.看QQ截图能够动态吸附直线的功能挺有意思,所以就模仿了一个. 先上效果图 界面很简单..呵呵 移动鼠标,会把鼠标所在最小矩形选中.把没有选中的地方给 ...

  6. thinkphp5开发规范(加强复习之前的)

    thinkphp5开发规范(加强复习之前的) 一.总结 一句话总结:和类相关的采用驼峰命名法:变量,函数,类,方法,属性采用驼峰命名发:数据库及文件及配置参数是小写字母加下划:常量大写加下划线 1.T ...

  7. 【codeforces 546C】Soldier and Cards

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  8. [Flow] Declare types for application

    In Flow, you can make global declarion about types. Run: flow init It will generate .flowconfig file ...

  9. 验证redis的快照和AOF

    http://blog.csdn.net/lichangzai/article/details/8692103 redis持久化简介: redis是一个支持持久化的内存数据库,也就是说redis需要经 ...

  10. Error: unrecognized flag -version

    按照网上的教程http://www.ituring.com.cn/article/13473 来确认node.js是否成功安装. 须要在命令行输入:node -version. 但是我在命令行输入:n ...