iOS开发UI篇—从代码的逐步优化看MVC

Posted 快乐加油站789

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发UI篇—从代码的逐步优化看MVC相关的知识,希望对你有一定的参考价值。

iOS开发UI篇—从代码的逐步优化看MVC

一、要求

要求完成下面一个小的应用程序。

二、一步步对代码进行优化

注意:在开发过程中,优化的过程是一步一步进行的。(如果一个人要吃五个包子才能吃饱,那么他是否直接吃第五个,前面四个不用吃就饱了?)

1.完成基本要求的代码(使用了字典转模型和xib连线)

(1)文件结构

(2)主要代码

  字典转模型部分:

 YYappInfo.h头文件

//
//  YYappInfo.h
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface YYappInfo : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *icon;
@property(nonatomic,strong,readonly)UIImage *img;

-(instancetype)initWithDict:(NSDictionary *)dict;
/**工厂方法*/
+(instancetype)appInfoWithDict:(NSDictionary *)dict;
@end

YYappInfo.m文件

//
//  YYappInfo.m
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYappInfo.h"
@interface YYappInfo()
{
    UIImage *_img;
}
@end
@implementation YYappInfo
-(instancetype)initWithDict:(NSDictionary *)dict
{
    if (self=[super init]) {
        self.name=dict[@"name"];
        self.icon=dict[@"icon"];
    }
    return self;
}

+(instancetype)appInfoWithDict:(NSDictionary *)dict
{
    return [[self alloc]initWithDict:dict];
}

-(UIImage *)img
{
    _img=[UIImage imageNamed:self.icon];
    return _img;
}
@end

xib部分(YYappInfoView.h文件):

注:(xib视图和YYappInfoView进行了关联,三个属性均进行了连线) 

//
//  YYappInfoView.h
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface YYappInfoView : UIView
@property (strong, nonatomic) IBOutlet UIImageView *appInfoViewimg;

@property (strong ,nonatomic) IBOutlet UILabel *appInfoViewlab;
@property (strong, nonatomic) IBOutlet UIButton *appInfoViewbtn;

@end

主要功能实现部分:

YYViewController.m文件

//
//  YYViewController.m
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYViewController.h"
#import "YYappInfo.h"
#import "YYappInfoView.h"

@interface YYViewController ()
@property(nonatomic,strong)NSArray *apps;
@end

//开发思路
//1.加载plist文件(字典转模型提供接口)
//2.使用xib文件完成单个的view
//3.计算坐标,使用for循环把view展现到界面上
//4.优化代码
@implementation YYViewController

//get方法,懒加载
-(NSArray *)apps
{
    if (!_apps) {
        NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];
        NSArray * arrayM = [NSArray arrayWithContentsOfFile:path];
        
        NSMutableArray *appinfoarray=[NSMutableArray array];
        for (NSDictionary *dict in arrayM) {
            [appinfoarray addObject:[YYappInfo appInfoWithDict:dict]];
        }
        _apps = appinfoarray;
    }
    return _apps;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"%d",self.apps.count);
    
    int totalloc = 3;
    CGFloat appviewW = 80;
    CGFloat appviewH = 90;
    CGFloat margin = (self.view.frame.size.width-totalloc*appviewW)/(totalloc+1);
    
    int count=self.apps.count;
    for (int i = 0; i < count; i++) {
        int row = i/totalloc;
        int loc = i%totalloc;
        
        CGFloat appviewX = margin + (margin + appviewW) * loc;
        CGFloat appviewY =  margin + (margin + appviewH) * row;
        
          YYappInfo *appinfo=self.apps[i];
        
        //拿出xib中的数据
        NSArray *arryM=[[NSBundle mainBundle]loadNibNamed:@"appInfoxib" owner:nil options:nil];
        YYappInfoView *appinfoview=[arryM firstObject];
        //设置位置
        appinfoview.frame=CGRectMake(appviewX, appviewY, appviewW, appviewH);
        //设置值
        appinfoview.appInfoViewimg.image=appinfo.img;
        appinfoview.appInfoViewlab.text=appinfo.name;
        //添加到视图
        appinfoview.appInfoViewbtn.tag=i;
        [appinfoview.appInfoViewbtn addTarget:self action:@selector(Click:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:appinfoview];
    }
}
-(void)Click:(UIButton *)btn
{
    btn.enabled=NO;
    YYappInfo *appinfo=self.apps[btn.tag];
    UILabel *lab=[[UILabel alloc]initWithFrame:CGRectMake(60, 450, 200, 20)];
    [lab setBackgroundColor:[UIColor lightGrayColor]];
    [lab setTextAlignment:NSTextAlignmentCenter];
    [lab setText:[NSString stringWithFormat:@"%@成功下载",appinfo.name]];
    [self.view addSubview:lab];
    
    lab.alpha=1.0;
    [UIView animateWithDuration:2.0 animations:^{
        lab.alpha=0;
    }completion:^(BOOL finished) {
        [lab removeFromSuperview];
    }];
}
@end

2.对1进行优化(把数据呈现部分封装到视图)

说明:在1的基础上寻找还会有那些可以优化的部分

1)改进思路:

(1)1中主文件的66~67行对控件属性的设置能否拿到视图中进行?

(2)1中61~62行是从xib文件中读取信息的操作,且和主控制器没有什么太大的关联,能否把它也封装到视图中进行?

(3)当上述两个步骤完成后,主文件69行以后的按钮操作和按钮单击事件就显得很突兀,放在主控制器中已经不再合适,是否可以把它放到视图中进行处理

2)按照上述思路优化后的代码如下:

  优化视图,在视图部分之对外提供一个接口,把数据的处理封装在内部

YYappInfoView.h文件代码:

//
//  YYappInfoView.h
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import <UIKit/UIKit.h>
@class YYappInfo;
@interface YYappInfoView : UIView

//读取
//+(instancetype)appInfoView;
//只对外开放一个数据接口
+(instancetype)appInfoViewWithappInfo:(YYappInfo *)appinfo;
@end

YYappInfoView.m文件代码

说明:该文件中的属性和click等均已做了连线的操作。

//
//  YYappInfoView.m
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYappInfoView.h"
#import "YYappInfo.h"
//私有扩展,把属性拿进来
@interface YYappInfoView ()
@property (strong, nonatomic) IBOutlet UIImageView *appInfoViewimg;
@property (strong ,nonatomic) IBOutlet UILabel *appInfoViewlab;
@property (strong, nonatomic) IBOutlet UIButton *appInfoViewbtn;
@property(strong,nonatomic)YYappInfo *appinfo;

@end
@implementation YYappInfoView

+(instancetype)appInfoView
{
    NSArray *arryM=[[NSBundle mainBundle]loadNibNamed:@"appInfoxib" owner:nil options:nil];
    YYappInfoView *appinfoview=[arryM firstObject];
    return appinfoview;
}

+(instancetype)appInfoViewWithappInfo:(YYappInfo *)appinfo
{
    YYappInfoView *appInfoView=[self appInfoView];
    appInfoView.appinfo=appinfo;
    return appInfoView;
}

-(void)setAppinfo:(YYappInfo *)appinfoc
{
    //这里一定要记录变化
    _appinfo=appinfoc;
    self.appInfoViewimg.image=appinfoc.img;
    self.appInfoViewlab.text=appinfoc.name;
}
- (IBAction)Click {
    
    self.appInfoViewbtn.enabled=NO;
    //YYappInfo *appinfo=self.apps[];
    
    YYappInfo *appinfo=self.appinfo;
    UILabel *lab=[[UILabel alloc]initWithFrame:CGRectMake(60, 450, 200, 20)];
    [lab setBackgroundColor:[UIColor lightGrayColor]];
    [lab setTextAlignment:NSTextAlignmentCenter];
    [lab setText:[NSString stringWithFormat:@"%@成功下载",appinfo.name]];
    //把lab添加到父视图(即view中)
    [self.superview addSubview:lab];
    
    lab.alpha=1.0;
    [UIView animateWithDuration:2.0 animations:^{
        lab.alpha=0;
    }completion:^(BOOL finished) {
        [lab removeFromSuperview];
    }];
}


@end

优化后的主控制器部分

YYViewController.m文件代码

 

//
//  YYViewController.m
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYViewController.h"
#import "YYappInfo.h"
#import "YYappInfoView.h"

@interface YYViewController ()
@property(nonatomic,strong)NSArray *apps;
@end
@implementation YYViewController

-(NSArray *)apps
{
    if (!_apps) {
        NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];
        NSArray * arrayM = [NSArray arrayWithContentsOfFile:path];
        
        NSMutableArray *appinfoarray=[NSMutableArray array];
        for (NSDictionary *dict in arrayM) {
            [appinfoarray addObject:[YYappInfo appInfoWithDict:dict]];
        }
        _apps = appinfoarray;
    }
    return _apps;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"%d",self.apps.count);
    
    int totalloc = 3;
    CGFloat appviewW = 80;
    CGFloat appviewH = 90;
    CGFloat margin = (self.view.frame.size.width-totalloc*appviewW)/(totalloc+1);
    
    int count=self.apps.count;
    for (int i = 0; i < count; i++) {
        int row = i/totalloc;
        int loc = i%totalloc;
        
        CGFloat appviewX = margin + (margin + appviewW) * loc;
        CGFloat appviewY =  margin + (margin + appviewH) * row;
        
        /*思路:
         要达到的效果 appinfoview.appinfo=appinfo;
        优化后即变成  appinfoview.appinfo=self.apps[i];
        要进行上面代码的操作,需要在视图中新增加一个appinfo类的属性,这样数据——》视图的转换即可以不需要在主控制器中完成,让程序结构一目了然
         */
        YYappInfo *appinfo=self.apps[i];
        YYappInfoView *appinfoview=[YYappInfoView appInfoViewWithappInfo:appinfo];
        //设置位置
        appinfoview.frame=CGRectMake(appviewX, appviewY, appviewW, appviewH);
        //添加
        [self.view addSubview:appinfoview];
    }
}
@end

3.对2进一步优化(把数据处理部分拿到模型中去进行)

(1)思路:把字典转模型部分的数据处理操作,拿到模型中去处理,这样外界不需要再关心数据处理的内部细节。

(2)优化后的代码如下

YYappInfo.h文件中向外开放一个接口,返回一个处理好的数组。

//
//  YYappInfo.h
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface YYappInfo : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *icon;
@property(nonatomic,strong)UIImage *img;

-(instancetype)initWithDict:(NSDictionary *)dict;
/**工厂方法*/
+(instancetype)appInfoWithDict:(NSDictionary *)dict;
+(NSArray *)appinfoarray;
@end

YYappInfo.m文件中的数据处理

//
//  YYappInfo.m
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYappInfo.h"
@interface YYappInfo()
@end
@implementation YYappInfo
-(instancetype)initWithDict:(NSDictionary *)dict
{
    if (self=[super init]) {
        self.name=dict[@"name"];
        self.icon=dict[@"icon"];
    }
    return self;
}

+(instancetype)appInfoWithDict:(NSDictionary *)dict
{
    return [[self alloc]initWithDict:dict];
}

-(UIImage *)img
{
    _img=[UIImage imageNamed:self.icon];
    return _img;
}

//把数据处理部分拿到模型中来处理
+(NSArray *)appinfoarray
{
    NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil];
    NSArray * arrayM = [NSArray arrayWithContentsOfFile:path];
    
    NSMutableArray *appinfoarray=[NSMutableArray array];
    for (NSDictionary *dict in arrayM) {
        [appinfoarray addObject:[YYappInfo appInfoWithDict:dict]];
    }
    return appinfoarray;
}
@end

主控制器中不再需要关心数据处理的内部细节

YYViewController.m文件现在只是负责模型和视图之间的协调工作了,怎么样?差不多了吧。

//
//  YYViewController.m
//  12-视图改进(1)
//
//  Created by apple on 14-5-25.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYViewController.h"
#import "YYappInfo.h"
#import "YYappInfoView.h"

@interface YYViewController ()
@property(nonatomic,strong)NSArray *apps;
@end
@implementation YYViewController

-(NSArray *)apps
{
    if (!_apps) {
        _apps=[YYappInfo appinfoarray];
    }
    return _apps;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    int totalloc = 3;
    CGFloat appviewW = 80;
    CGFloat appviewH = 90;
    CGFloat margin = (self.view.frame.size.width-totalloc*appviewW)/(totalloc+1);
    
    int count=self.apps.count;
    for (int i = 0; i < count; i++) {
        
        int row = i/totalloc;
        int loc = i%totalloc;
        
        CGFloat appviewX = margin + (margin + appviewW) * loc;
        CGFloat appviewY =  margin + (margin + appviewH) * row;
        
        YYappInfo *appinfo=self.apps[i];
        YYappInfoView *appinfoview=[YYappInfoView appInfoViewWithappInfo:appinfo];
        appinfoview.frame=CGRectMake(appviewX, appviewY, appviewW, appviewH);
        [self.view addSubview:appinfoview];
    }
}
@end

实现效果:

 

4.补充说明

 View的封装思路

(1) 如果一个view内部的子控件比较多,一般会考虑自定义一个view,把它内部子控件的创建屏蔽起来,不让外界关心

(2) 外界可以传入对应的模型数据给view,view拿到模型数据后给内部的子控件设置对应的数据

三、mvc机制简单说明

说明:

(1)在开发过程中,作为控制器处理的量级应该很轻,不该操心的不操心。协调好模型和视图就ok了,要学会当一个好老板。

(2)三个部分各司其职,数据模型只负责数据的处理,视图部分只负责把拿到的数据进行显示,两个部分都是被动的,等待着大管家控制器的调遣。

(3)在OC中,如果视图和数据模型之间有通道,那控制器是否处于失控状态呢?

以上是关于iOS开发UI篇—从代码的逐步优化看MVC的主要内容,如果未能解决你的问题,请参考以下文章

从JVM的角度看JAVA代码--代码优化

iOS开发UI篇—字典转模型

iOS开发UI篇—字典转模型

iOS开发UI篇—UITableviewcell的性能优化和缓存机制

iOS开发UI篇—懒加载

iOS开发UI篇—懒载入