相信大家对MVC这种模式都很熟悉,但是真正理解的并不是很多,也包括我,通俗地说model是数据模型,view是视图模型(一般包含model模型),而controller就是控制model和view的桥梁,有传递数据(传给model)功能,也能控制view(包括大小及位置,展示的样式等等)。
下面就通过一个小小的例子来理解一下MVC怎样使用,并能动态计算view的高度?
首先我们看看整个例子的框架图:
然后我们具体实现的view是这样的
开始动工
Model
两个数据模型:Person和PersonFrame都继承NSObject
FTYPersonModel.h
#import <Foundation/Foundation.h>
@interface FTYPersonModel : NSObject
/**
* 头像名
*/
@property (copy, nonatomic) NSString *icon;
/**
* 姓名
*/
@property (copy, nonatomic) NSString *name;
/**
* 介绍
*/
@property (copy, nonatomic) NSString *intro;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)personModelWithDict:(NSDictionary *)dict;
@end
FTYPersonModel.m
#import "FTYPersonModel.h"
@implementation FTYPersonModel
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self == [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)personModelWithDict:(NSDictionary *)dict{
return [[FTYPersonModel alloc] initWithDict:dict];
}
@end
PersonModel模型作用是声明属性
FTYPersonFrameModel.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class FTYPersonModel;
@interface FTYPersonFrameModel : NSObject
/**
* person模型
*/
@property (strong, nonatomic) FTYPersonModel *person;
/**
* 头像Frame
*/
@property (assign, nonatomic, readonly) CGRect iconFrame;
/**
* 名称Frame
*/
@property (assign, nonatomic, readonly) CGRect nameFrame;
/**
* 介绍Frame
*/
@property (assign, nonatomic, readonly) CGRect introFrame;
/**
* view的高度
*/
@property (assign, nonatomic, readonly) CGFloat viewHeight;
@end
FTYPersonFrameModel包含一个personModel
FTYPersonFrameModel.m
#import "FTYPersonFrameModel.h"
#import "FTYPersonModel.h"
@implementation FTYPersonFrameModel
/**
* 重置person
*
* @param person person
*/
- (void)setPerson:(FTYPersonModel *)person{
_person = person;
CGFloat mainWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat margin = 10;
CGFloat iconWH = 70;
_iconFrame = CGRectMake(margin, margin, iconWH, iconWH);
CGFloat nameX = CGRectGetMaxX(_iconFrame)+margin;
CGFloat maxWidth = mainWidth - margin*2 - iconWH - margin;
CGSize size = CGSizeMake(maxWidth, MAXFLOAT);
CGSize nameSize = [self sizeCustomWithString:person.name font:[UIFont systemFontOfSize:16.0] constrainedToSize:size];
_nameFrame = CGRectMake(nameX, margin, nameSize.width, nameSize.height);
CGFloat introX = nameX;
CGFloat introY = CGRectGetMaxY(_nameFrame)+margin;
CGSize introSize = [self sizeCustomWithString:person.intro font:[UIFont systemFontOfSize:14.0] constrainedToSize:size];
_introFrame = CGRectMake(introX, introY, introSize.width, introSize.height);
if (CGRectGetMaxY(_introFrame) > CGRectGetMaxY(_iconFrame)) {
_viewHeight = CGRectGetMaxY(_introFrame) + margin;
} else {
_viewHeight = CGRectGetMaxY(_iconFrame) + margin;
}
}
/**
* 获取最大宽度的字符串size
*
* @param str 字符串
* @param font 字体大小
* @param constrainedToSize 最大区域
*
* @return size
*/
- (CGSize)sizeCustomWithString:(NSString *)str font:(UIFont *)font constrainedToSize:(CGSize)size
{
CGSize resultSize;
if([self respondsToSelector:@selector(sizeWithAttributes:)]){
NSDictionary *attributes = @{NSFontAttributeName: font};
CGRect rect = [str boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil];
resultSize.width = ceilf(rect.size.width);
resultSize.height = ceilf(rect.size.height);
}else{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
resultSize = [str sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping];
#pragma clang diagnostic pop
}
return resultSize;
}
@end
FTYPersonFrameModel的作用就是通过person的值来具体计算出控件所需要的宽度和高度,并赋值给对应的frame;
View
接着我们新建一个视图
FTYPersonView.h
#import <UIKit/UIKit.h>
@class FTYPersonFrameModel;
@interface FTYPersonView : UIView
/**
* personFrame模型
*/
@property (strong, nonatomic) FTYPersonFrameModel *personFrame;
@end
FTYPersonView.m
#import "FTYPersonView.h"
#import "FTYPersonFrameModel.h"
#import "FTYPersonModel.h"
@interface FTYPersonView()
/**
* 头像
*/
@property (weak, nonatomic) UIImageView *iconView;
/**
* 名称
*/
@property (weak, nonatomic) UILabel *nameLabel;
/**
* 介绍
*/
@property (weak, nonatomic) UILabel *introLabel;
@end
@implementation FTYPersonView
- (instancetype)initWithFrame:(CGRect)frame{
if (self == [super initWithFrame:frame]) {
self.backgroundColor = [UIColor grayColor];
// 初始化控件
UIImageView *imageView = [[UIImageView alloc] init];
_iconView = imageView;
[self addSubview:imageView];
UILabel *name = [[UILabel alloc] init];
name.numberOfLines = 0;
name.backgroundColor = [UIColor redColor];
name.font = [UIFont systemFontOfSize:16.0];
_nameLabel = name;
[self addSubview:name];
UILabel *intro = [[UILabel alloc] init];
intro.numberOfLines = 0;
intro.font = [UIFont systemFontOfSize:14.0];
intro.backgroundColor = [UIColor redColor];
_introLabel = intro;
[self addSubview:intro];
}
return self;
}
/**
* 重置personFrame
*
* @param personFrame personFrame
*/
- (void)setPersonFrame:(FTYPersonFrameModel *)personFrame{
_personFrame = personFrame;
FTYPersonModel *person = personFrame.person;
if (person.icon.length > 0 ) {
self.iconView.image = [UIImage imageNamed:person.icon];
} else {
self.iconView.image = [UIImage imageNamed:@"center_add_friend"];
}
self.iconView.frame = personFrame.iconFrame;
if (person.name.length > 0) {
self.nameLabel.text = person.name;
} else {
self.nameLabel.text = @"匿名";
}
self.nameLabel.frame = personFrame.nameFrame;
self.introLabel.text = person.intro;
self.introLabel.frame = personFrame.introFrame;
}
@end
视图view初始化所需要的控件,然后通过一个personFrame属性传值,重置这个属性,获取相对应得值和frame,并设置值和frame。
Controller
FTYPersonViewController.h
#import <UIKit/UIKit.h>
@interface FTYPersonViewController : UIViewController
@end
FTYPersonViewController.m
#import "FTYPersonViewController.h"
#import "FTYPersonFrameModel.h"
#import "FTYPersonModel.h"
#import "FTYPersonView.h"
@interface FTYPersonViewController ()
@end
@implementation FTYPersonViewController
- (void)viewDidLoad {
[super viewDidLoad];
FTYPersonModel *person = [[FTYPersonModel alloc] init];
person.icon = nil;
person.name = @"圣诞节不发了苏打粉";
person.intro = @"风把类似的反感本拉萨的高发速度跟法师不东风公司的发放第三方把死灵法大陆is发生的部分就爱上当老板发生";
FTYPersonFrameModel *personFrame = [[FTYPersonFrameModel alloc] init];
personFrame.person = person;
FTYPersonView *personView = [[FTYPersonView alloc] init];
personView.personFrame = personFrame;
personView.frame = CGRectMake(0, 100, self.view.frame.size.width, personFrame.viewHeight);
[self.view addSubview:personView];
}
FTYPersonViewController初始化数据,并设置view的frame。
效果
包结构
总结:这里可能写的不是很清楚,也非常简单,但是如果你能理解的话,那就进步了不少。其实MVC模式也并不是要理解非常清楚,有时候用多了也就理解了,大家都按照这样的规范写代码,自然而然就形成一种很好的规范,MVC也能很好的理解。