lauch图加载完成后,开始做tabbar控制器的部分
- (void)createTabBarController {
UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
}
将这个view 改成tabbarcontroller,发现tabbar控制器放在子文件夹里面,于是创建所有必须的文件夹,并add到项目里面去,将QMUIConfigurationTemplate
也移动到configure文件夹下
新建 QDTabBarViewController
继承自 QMUITabBarViewController
使用的是QMUI的模板 发现里面没写什么内容 。还需要添加一个导航控制器QDNavigationController
,注意到也是一行没写。
然后是导航栏下的根控制器 QDUIKitViewController
发现继承关系
-
QMUICommonViewController
-
– QDCommonViewController
-
– QDCommonGridViewController
- – QDUIKitViewController
-
– QDCommonGridViewController
-
– QDCommonViewController
于是按顺序创建 这三个子类 控制器
首先是 QDCommonViewController
只设置了背景色 self.view.backgroundColor = UIColorWhite;
然后是QDCommonGridViewController
#import "QDCommonViewController.h"
@interface QDCommonGridViewController : QDCommonViewController
@property(nonatomic, strong) QMUIOrderedDictionary *dataSource;//重写字典做数据源
@property(nonatomic, strong, readonly) QMUIGridView *gridView; //九宫格图表
@end
@interface QDCommonGridViewController (UISubclassingHooks) //子类钩子类别
// 子类继承,可以不调super
- (void)initDateSource;//初始化数据源
- (void)didSelectCellWithTitle:(NSString *)title;//选中cell以及cell的标题
@end
这是通用九宫格的父类,可以看到数据源是自定义的字典,ui是自定义view,使用方式应该是依靠子类调用category来实现的。先不深究QMUIOrderedDictionary
和QMUIGridView
这两个自定义类,在实现里面具体看这两个类的使用。
#import "QDCommonGridViewController.h"
@interface QDCommonGridViewController ()
@property(nonatomic, strong) UIScrollView *scrollView;
@end
@interface QDCommonGridButton : QMUIButton
@end
@implementation QDCommonGridViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
[self initDateSource];
}
return self;
}
- (void)initSubviews {
[super initSubviews];
self.scrollView = [[UIScrollView alloc] init];
[self.view addSubview:self.scrollView];
_gridView = [[QMUIGridView alloc] init];
for (NSInteger i = 0 , I = self.dataSource.count; i<I; i++) {
[self.gridView addSubview:[self generateButtonAtIndex:i]];
}
[self.scrollView addSubview:self.gridView];
}
- (QDCommonGridButton *)generateButtonAtIndex:(NSInteger)index{
NSString *keyName = self.dataSource.allKeys[index]; //key数组
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:keyName attributes:
@{NSForegroundColorAttributeName: UIColorGray, NSFontAttributeName: UIFontMake(11), NSParagraphStyleAttributeName: [NSMutableParagraphStyle qmui_paragraphStyleWithLineHeight:12 lineBreakMode:NSLineBreakByTruncatingTail textAlignment:NSTextAlignmentCenter]}];
UIImage *image = (UIImage *)[self.dataSource objectForKey:keyName];
QDCommonGridButton *button = [[QDCommonGridButton alloc] init];
[button setAttributedTitle:attributedString forState:UIControlStateNormal];
[button setImage:image forState:UIControlStateNormal];
button.tag = index;
[button addTarget:self action:@selector(handleGirdButtonEvent:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
- (void)handleGirdButtonEvent:(QDCommonGridButton *)button {
NSString *keyName = self.dataSource.allKeys[button.tag]; //key数组
[self didSelectCellWithTitle:keyName];
}
以上代码是初始化界面,初始化子界面,初始化子界面的button,给button添加响应事件,其中
[self initDateSource];
[self didSelectCellWithTitle:keyName];
都调用了QDCommonGridViewController (UISubclassingHooks)分类的方法,虽然方法还没实现,不影响调用。
@implementation QDCommonGridViewController (UISubclassingHooks)
- (void)initDataSource {
}
- (void)didSelectCellWithTitle:(NSString *)title {
}
这个分类写法很特别,先是给控制器添加分类,同时在分类实现中不添加任何代码。应该是要子类化中重写实现,看方法名很容易理解到,是要在这两个方法里面设置数据源,以及点击回传的事件,这样就实现了界面和逻辑的分离。这种写法和直接写方法又有什么不同呢?
NSString *keyName = self.dataSource.allKeys[index]; //key数组`
说明数据源的keyname就是title,index是数据源的第几个,
`UIImage *image = (UIImage *)[self.dataSource objectForKey:keyName];`
说明title 对应的value是image的对象。
然后是创建自定义button
@implementation QDCommonGridButton
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10);
self.imageView.contentMode = UIViewContentModeCenter;
self.titleLabel.numberOfLines = 2;
self.highlightedBackgroundColor = TableViewCellSelectedBackgroundColor;
self.qmui_needsTakeOverTouchEvent = YES; // 解决滚动视图和点击视图延时问题
self.qmui_borderPosition = QMUIBorderViewPositionRight | QMUIImageBorderPositionBottom;
}
return self;
}
- (void)layoutSubviews {
// 不用父类的, 自己计算
[super layoutSubviews];
if (CGRectIsEmpty(self.bounds)) {
return;
}
CGSize contentSize = CGSizeMake(CGRectGetWidth(self.bounds) - UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets),
CGRectGetHeight(self.bounds) - UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets));
CGPoint center = CGPointMake(flat(self.contentEdgeInsets.left + contentSize.width/2),flat(self.contentEdgeInsets.top + contentSize.height / 2));
self.imageView.center = CGPointMake(center.x, center.y - 12);
CGSize titleLabelSize = [self.titleLabel sizeThatFits:contentSize];
self.titleLabel.frame = CGRectFlatMake(self.contentEdgeInsets.left, center.y + PreferredVarForDevices(27, 27, 21, 21), contentSize.width, titleLabelSize.height);
}
这里有两个关键点
self.qmui_needsTakeOverTouchEvent = YES; // 解决滚动视图和点击视图延时问题
进去看了一下是对button的很多事件做了优化,防止二次点击,手势响应冲突等,没有细看
CGSize contentSize = CGSizeMake(CGRectGetWidth(self.bounds) - UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets), CGRectGetHeight(self.bounds) - UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets));
重新设置了label的contsize 应该是保证label和父类的边距问题。
CGPoint center = CGPointMake(flat(self.contentEdgeInsets.left + contentSize.width / 2), flat(self.contentEdgeInsets.top + contentSize.height / 2));
CGSize titleLabelSize = [self.titleLabel sizeThatFits:contentSize];
重新设置了contentsize以后的中心点
self.titleLabel.frame = CGRectFlatMake(self.contentEdgeInsets.left, center.y + PreferredVarForDevices(27, 27, 21, 21), contentSize.width, titleLabelSize.height);
然后重写了frame 这里注意到 有用到flat
里面是像素级的适配,具体还要再细看,可以看出对适配的严谨。
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.scrollView.frame = self.view.bounds;
if(CGRectGetWidth(self.view.bounds) <= [QMUIHelper screenSizeFor55Inch].width){
self.gridView.columnCount = 3;
CGFloat itemWidth = flat(CGRectGetWidth(self.scrollView.bounds) / self.gridView.columnCount);
self.gridView.rowHeight = itemWidth;
} else {
CGFloat minimumItemWidth = flat([QMUIHelper screenSizeFor55Inch].width /3.0);
CGFloat maximumItemWidth = flat(CGRectGetWidth(self.view.bounds))/5.0;
CGFloat freeSpacingWhenDisplayingMinimumItemCount = CGRectGetWidth(self.scrollView.bounds) / maximumItemWidth - floor(CGRectGetWidth(self.scrollView.bounds)/maximumItemWidth);
CGFloat freeSpacingWhenDisplayingMaximumItemCount = CGRectGetWidth(self.scrollView.bounds) / minimumItemWidth - floor(CGRectGetWidth(self.scrollView.bounds)/minimumItemWidth);
if (freeSpacingWhenDisplayingMinimumItemCount < freeSpacingWhenDisplayingMaximumItemCount) {
// 按照每行最少item的情况来布局的话,空间利用率会更高,所以按照最少item来
self.gridView.columnCount = floor(CGRectGetWidth(self.scrollView.bounds) / maximumItemWidth);
CGFloat itemWidth = floor(CGRectGetWidth(self.scrollView.bounds) / self.gridView.columnCount);
self.gridView.rowHeight = itemWidth;
} else {
self.gridView.columnCount = floor(CGRectGetWidth(self.scrollView.bounds) / minimumItemWidth);
CGFloat itemWidth = floor(CGRectGetWidth(self.scrollView.bounds) / self.gridView.columnCount);
self.gridView.rowHeight = itemWidth;
}
}
CGFloat gridViewHeight = [self.gridView sizeThatFits:CGSizeMake(CGRectGetWidth(self.scrollView.bounds), CGFLOAT_MAX)].height;
self.gridView.frame = CGRectMake(0, 0, CGRectGetWidth(self.scrollView.bounds), gridViewHeight);
self.scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.scrollView.bounds), CGRectGetMaxY(self.gridView.frame));
}
这段代码设置了self.gridView
的子视图的一些参数
[QMUIHelper screenSizeFor55Inch].width
这是适配竖屏手机,直接设置,其余部分应该是适配横屏和ipad,
使用了floor
向下取整 CGFLOAT
取最大值,CGRectGetMaxY
取最大值,很多地方都有类似处理