Masonry,是一个基于纯代码的AutoLayout库.初次涉及时,只是感觉它很方便,既有Xib的易读性,又有纯代码的灵活性.试用一段时间之后,突然想到: 或许借助Masonry,建立一个纯代码的不依赖Xib的AutoLayout视图组件机制.
GitHub地址:https://github.com/SnapKit/Masonry
Masonry安装:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, "8.0"
use_frameworks!
target :testMasonry
pod 'Masonry'
目前能得到的效果
- 视图基于 AutoLayout;
- 视图自动适配不同屏幕尺寸;
- 视图完全独立于数据与业务逻辑;
- 视图严肃仅与父视图有位置关系;
- 可以将视图模块的元素与模块同名属性自动关联;
- 仅需知道父视图的宽高,模块内某一个UI元素的宽高, UI元素的 bottom 与 right, 就可以唯一确定任意元素的位置.
核心理论基础: AutoLayout中,如何唯一确定元素在不同尺寸屏幕上的位置?
既定方案,必须基于AutoLayout,至于AutoLayout与Frame的区别于优势,不做赘述.
在不考虑多屏幕兼容的情况下, AutoLayout,可以直接使用固定的约束常量值来确定,但是 马上iPhone 7 都要出来了,指不定什么尺寸呢? 一个机型,一个UI代码?是不是想想都让人头大!
考虑到多屏幕尺寸,UI设计图等比缩放的常用情况,我分享一个可以唯一确定UI元素的方案:
[subView makeConstraints:^(MASConstraintMaker *make) {
UIView * superView = subView.superview;
make.width.equalTo(superView).multipliedBy(subWidth / superWidth);
make.height.equalTo(superView).multipliedBy(subHeight / superHeight);
make.right.equalTo(superView).multipliedBy(subRight / superWidth);
make.bottom.equalTo(superView).multipliedBy(subBottom / superHeight);
}];
以上代码,是整个代码的核心,其巧妙之处在于:不使用constant,而是使用比例来指定约束.选取的是 width,height,right,bottom,而不是其他属性,其巧妙之处,大家试用下其他属性就知道了.
核心代码,打造自己的视图模块库.
直接继承YFViewComponent类,然后实现类方法 subViewsConfig 即可.
//
// YFViewComponent.h
// testMasonry
//
// Created by xiao on 16/6/6.
// Copyright © 2016年 xiao. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
* 预定义常量的声明.
*/
//!< 同一设计图中,视图模块本身的宽度.
extern const NSString * YFViewComponentSelfHolderWidthKey;
//!< 同一设计图中,视图模块本身的高度.
extern const NSString * YFViewComponentSelfHolderHeightKey;
//!< 同一设计图中,模块的所有子视图.
extern const NSString * YFViewComponentSubViewsKey;
//!< 子视图的类型.
extern const NSString * YFViewComponentSubViewClassNameKey;
//!< 子视图对应的属性,模块中应有属性与其对应,且可通过此属性访问对应的子视图.
extern const NSString * YFViewComponentSubViewPropNameKey;
//!< 同一设计图中,子视图的宽度.
extern const NSString * YFViewComponentSubViewHolderWidthKey;
//!< 同一设计图中,子视图的高度.
extern const NSString * YFViewComponentSubViewHolderHeightKey;
//!< 同一设计图中,子视图的右内边距值(right).
extern const NSString * YFViewComponentSubViewHolderRightKey;
//!< 同一设计图中,子视图的底部边距值(bottom).
extern const NSString * YFViewComponentSubViewHolderBottomKey;
@interface YFViewComponent : UIView
/**
* 子视图配置信息.
*
* 子类应重写覆盖此方法.
*
一个示例:
@{
YFViewComponentSelfHolderWidthKey: @640.0,
YFViewComponentSelfHolderHeightKey: @155.0,
YFViewComponentSubViewsKey:
@[@{
YFViewComponentSubViewClassNameKey: NSStringFromClass([UIImageView class]) ,
YFViewComponentSubViewPropNameKey: @"imageView",
YFViewComponentSubViewHolderWidthKey: @160,
YFViewComponentSubViewHolderHeightKey: @120,
YFViewComponentSubViewHolderBottomKey: @140,
YFViewComponentSubViewHolderRightKey: @180
}]
}
*
* @return 返回子视图的配置信息.
*/
+ (NSDictionary *) subViewsConfig;
@end
//
// YFViewComponent.m
// testMasonry
//
// Created by xiao on 16/6/6.
// Copyright © 2016年 xiao. All rights reserved.
//
#import "YFViewComponent.h"
#import "Masonry.h"
/**
* 预定义常量的定义.
*/
const NSString * YFViewComponentSelfHolderWidthKey = @"YFViewComponentSelfHolderWidthKey";
const NSString * YFViewComponentSelfHolderHeightKey = @"YFViewComponentSelfHolderHeightKey";
const NSString * YFViewComponentSubViewsKey = @"YFViewComponentSubViewsKey";
const NSString * YFViewComponentSubViewClassNameKey = @"YFViewComponentSubViewClassNameKey";
const NSString * YFViewComponentSubViewPropNameKey = @"YFViewComponentSubViewPropNameKey";
const NSString * YFViewComponentSubViewHolderWidthKey = @"YFViewComponentSubViewHolderWidthKey";
const NSString * YFViewComponentSubViewHolderHeightKey = @"YFViewComponentSubViewHolderHeightKey";
const NSString * YFViewComponentSubViewHolderRightKey = @"YFViewComponentSubViewHolderRightKey";
const NSString * YFViewComponentSubViewHolderBottomKey = @"YFViewComponentSubViewHolderBottomKey";
@implementation YFViewComponent
- (instancetype)init
{
self = [super init];
if (nil != self) {
UIView * holderView = self;
NSDictionary * config = [[self class] subViewsConfig];
CGFloat superHeight = [[config objectForKey: YFViewComponentSelfHolderHeightKey] floatValue];
CGFloat superWidth = [[config objectForKey: YFViewComponentSelfHolderWidthKey] floatValue];;
NSArray * locatArray = [config objectForKey: YFViewComponentSubViewsKey];
[locatArray enumerateObjectsUsingBlock:^(NSDictionary * obj, NSUInteger idx, BOOL *stop) {
NSString * classString = [obj objectForKey: YFViewComponentSubViewClassNameKey];
Class viewClass = NSClassFromString(classString);
if (YES != [viewClass isSubclassOfClass:[UIView class]]) {
return;
}
UIView * subView = [[viewClass alloc] init];
[holderView addSubview: subView];
NSString * viewKey = [obj objectForKey: YFViewComponentSubViewPropNameKey];
[holderView setValue: subView forKey: viewKey];
CGFloat subWidth = [[obj objectForKey: YFViewComponentSubViewHolderWidthKey] floatValue];
CGFloat subHeight = [[obj objectForKey: YFViewComponentSubViewHolderHeightKey] floatValue];
CGFloat subBottom = [[obj objectForKey: YFViewComponentSubViewHolderBottomKey] floatValue];
CGFloat subRight = [[obj objectForKey: YFViewComponentSubViewHolderRightKey] floatValue];
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
UIView * superView = subView.superview;
make.width.equalTo(superView).multipliedBy(subWidth / superWidth);
make.height.equalTo(superView).multipliedBy(subHeight / superHeight);
make.right.equalTo(superView).multipliedBy(subRight / superWidth);
make.bottom.equalTo(superView).multipliedBy(subBottom / superHeight);
}];
}];
}
return self;
}
+ (NSDictionary *) subViewsConfig{
return nil;
}
@end
一个示例: 仿网易新闻的新闻单元格.
这个示例,取材自网易新闻.图示中已经标注了单元格的宽高,单元格内各个UI元素的width,height,bottom,right.此处UI设计师可根据屏幕尺寸出图,我们根据一份跟定的设计图,直接使用 MarkMan(一个非常好用的标准工具)丈量标记即可. 因为我们是基于比例来添加约束,不同屏幕下,会自动等比变换.
这是一个简单的示例,为了方便演示,临时加上了:
//
// YFAutoTransView.h
// testMasonry
//
// Created by xiao on 16/6/6.
// Copyright © 2016年 xiao. All rights reserved.
//
#import "YFViewComponent.h"
@interface YFAutoTransView : YFViewComponent
@property (weak, nonatomic) UIImageView * imageView;
@property (weak, nonatomic) UILabel * titleLabel;
@property (weak, nonatomic) UILabel * detailLabel;
@property (weak, nonatomic) UIButton * chatBtn;
@end
//
// YFAutoTransView.m
// testMasonry
//
// Created by xiao on 16/6/6.
// Copyright © 2016年 xiao. All rights reserved.
//
#import "YFAutoTransView.h"
@implementation YFAutoTransView
+ (NSDictionary *) subViewsConfig{
NSNumber * holderWidth = @640.0;
NSNumber * holderHeight = @155.0;
NSArray * subConfig = @[
@[NSStringFromClass([UIImageView class]), @"imageView",
@160, @120, @140, @180],
@[NSStringFromClass([UILabel class]), @"titleLabel",
@420, @31, @55, @615],
@[NSStringFromClass([UILabel class]), @"detailLabel",
@410, @60, @136, @605],
@[NSStringFromClass([UIButton class]), @"chatBtn",
@120, @32, @141, @628]];
NSMutableArray * subViewsConfig = [NSMutableArray arrayWithCapacity: 42];
[subConfig enumerateObjectsUsingBlock:^(NSArray * obj, NSUInteger idx, BOOL *stop) {
if (6 != obj.count) {
return;
}
NSDictionary * configDict =
@{
YFViewComponentSubViewClassNameKey: obj[0],
YFViewComponentSubViewPropNameKey: obj[1],
YFViewComponentSubViewHolderWidthKey: obj[2],
YFViewComponentSubViewHolderHeightKey: obj[3],
YFViewComponentSubViewHolderBottomKey: obj[4],
YFViewComponentSubViewHolderRightKey: obj[5]
};
[subViewsConfig addObject: configDict];
}];
NSDictionary * config = @{
YFViewComponentSelfHolderWidthKey: holderWidth,
YFViewComponentSelfHolderHeightKey: holderHeight,
YFViewComponentSubViewsKey: subViewsConfig};
return config;
}
@end
这是与数据结合之后的效果图.只是个初稿,还需要进一步调试.也就是说,以后再写UI界面,你的注意力将可以集中在 数据与视图本身的交互处理上.
//
// ViewController.m
// testMasonry
//
// Created by xiao on 16/6/6.
// Copyright © 2016年 xiao. All rights reserved.
//
#import "ViewController.h"
#import "YFAutoTransView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
YFAutoTransView * autoTestView = [[YFAutoTransView alloc] init];
autoTestView.backgroundColor = [UIColor grayColor];
autoTestView.frame = CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 155.0/2);
autoTestView.imageView.image = [UIImage imageNamed:@"autoTrans.png"];
autoTestView.imageView.backgroundColor = [UIColor yellowColor];
autoTestView.titleLabel.text = @"爱马仕版苹果表开售8688元起";
autoTestView.titleLabel.font = [UIFont systemFontOfSize:15];
[autoTestView.titleLabel adjustsFontSizeToFitWidth];
autoTestView.detailLabel.text = @"爱马仕版苹果表盘和表带并不会单独销售.";
autoTestView.detailLabel.numberOfLines = 0;
autoTestView.detailLabel.font = [UIFont systemFontOfSize:12];
[autoTestView.chatBtn setTitle:@"跟帖" forState: UIControlStateNormal];
autoTestView.chatBtn.backgroundColor = [UIColor greenColor];
[self.view addSubview: autoTestView];
}
@end
运行结果: