iOS新闻客户端开发教程3--基类编写

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JaCman/article/details/49560647

在面向对象编程中,常用的就是抽象和封装,今天来完成Model,ViewModel,ViewController的编写。

日志宏定义

为了更好查看日志信息,我们封装了一些宏定义,取代了NSLog,让日志信息更方便我们调试
新建Defineds.h


#if (DEBUG || TESTCASE)
#define Log(format, ...) NSLog(format, ## __VA_ARGS__)
#else
#define Log(format, ...)
#endif

// 日志输出宏
#define BASE_LOG(cls,sel) Log(@"%@-%@",NSStringFromClass(cls), NSStringFromSelector(sel))
#define BASE_ERROR_LOG(cls,sel,error) Log(@"ERROR:%@-%@-%@",NSStringFromClass(cls), NSStringFromSelector(sel), error)
#define BASE_INFO_LOG(cls,sel,info) Log(@"INFO:%@-%@-%@",NSStringFromClass(cls), NSStringFromSelector(sel), info)

// 日志输出函数
#if (DEBUG || TESTCASE)
#define BASE_LOG_FUN()         BASE_LOG([self class], _cmd)
#define BASE_ERROR_FUN(error)  BASE_ERROR_LOG([self class],_cmd,error)
#define BASE_INFO_FUN(info)    BASE_INFO_LOG([self class],_cmd,info)
#else
#define BASE_LOG_FUN()
#define BASE_ERROR_FUN(error)
#define BASE_INFO_FUN(info)
#endif

Model基类

在本项目中,Model命名规则都以”对象名称”+Info来命名,我们编写Model基类,新建BaseInfo类
BaseInfo.h

#import <Foundation/Foundation.h>

@interface BaseInfo : NSObject
@property(nonatomic,strong) NSString *ID;
@property(nonatomic,strong) NSString *name;
/// 初始化方法
-(instancetype) initWithDict:(NSDictionary *) dict;
/// 初始化方法
+(instancetype) infoWithDict:(NSDictionary *) dict;
/// 字典转NSArray
+(NSArray *) arrayFromDict:(NSDictionary *) dict;
/// 将数组转成对象数组
+(NSArray *) arrayFromArray:(NSArray *) array;

@end

BaseInfo.m

#import "BaseInfo.h"

@implementation BaseInfo

-(instancetype) initWithDict:(NSDictionary *)dict
{
    if (self = [super init]) {
        self.ID = [dict objectForKey:@"id"];
        self.name = [dict objectForKey:@"name"];
        //[self setValuesForKeysWithDictionary:dict];简洁写法
    }
    return self;
}

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

+(NSArray *) arrayFromDict:(NSDictionary *) dict
{
    NSArray *array = [dict objectForKey:NetData];
    return [self arrayFromArray:array];
}

+(NSArray *) arrayFromArray:(NSArray *) array
{
    NSMutableArray *infos = [[NSMutableArray alloc] init];
    for (NSDictionary *dict in array) {
        [infos addObject:[self infoWithDict:dict]];
    }

    if(infos.count == 0){
        return nil;
    }

    return infos;
}


@end

Model一般是负责数据传输,充当DTO的角色,它往往是请求服务器接口的返回结果的封装,假设我们服务器的程序接口返回的数据包含ID和name参数,那么我们定义类时,也同样定义2个属性ID和name。
在BaseInfo中,定义了以NSDictionary初始化对象的方法infoWithDict,arrayFromDict,也是OC中常用的初始化对象方法之一。

ViewModel基类

ViewModel负责View和Model间的数据转换,常用的业务操作等。我们在这里定义它是的使命是负责网络请求,并把返回结果封装成Model类。
定义BaseOpertation类
BaseOpertation.h

#import <Foundation/Foundation.h>
@protocol BaseOperationDelegate;
@interface BaseOperation : NSObject{//声明变量,不能通过点语法设置和获取变量
    id<BaseOperationDelegate> _delegate;
    NSURLConnection *_connection;
    NSMutableData *_receiveData;//NSMutableData可变的字节操作类
    NSInteger _statusCode;
    long long _totalLength;//双长整型
    //默认是@protected
@public NSDictionary *_opInfo;
}

-(id)initWithDelegate:(id<BaseOperationDelegate>) delegate opInfo:(NSDictionary *) opInfo;
-(NSMutableURLRequest *) urlRequest;
- (void)executeOp;
- (void)cancelOp;
- (void)parseData:(id)data;
- (void)parseSuccess:(NSDictionary *)dict jsonString:(NSString *)jsonString;
- (void)parseFail:(id)dict;
- (void)parseProgress:(long long)receivedLength;
- (NSTimeInterval)timeoutInterval;

@end


@protocol BaseOperationDelegate <NSObject>

-(void)opSuccess:(id)data;
-(void)opFail:(NSString *) errorMsg;

@optional
- (void)opSuccessEx:(id)data opinfo:(NSDictionary *)dictInfo;
- (void)opFailEx:(NSString *)errorMessage opinfo:(NSDictionary *)dictInfo;
- (void)opSuccessMatch:(id)data;
- (void)opUploadSuccess;

@end

BaseOperation.m

#import "BaseOperation.h"

@implementation BaseOperation

-(id)initWithDelegate:(id<BaseOperationDelegate>)delegate opInfo:(NSDictionary *)opInfo
{
    if(self = [super init]){
        _delegate = delegate;
        _opInfo = opInfo;
        _totalLength = 0;
    }
    return self;
}

- (void)cancelOp
{
    if (_connection != nil) {
        BASE_INFO_FUN(@"_connection dealloc cancel");
        [_connection cancel];
    }
    _connection = nil;
}

- (void)dealloc
{
    if (_connection != nil) {
        BASE_INFO_FUN(@"_connection dealloc cancel");
        [_connection cancel];
    }
    _connection = nil;
    _delegate = nil;
}

- (NSTimeInterval)timeoutInterval
{
    return FxRequestTimeout;
}

-(NSMutableURLRequest *)urlRequest
{
    NSString *urlString = [_opInfo objectForKey:@"url"];
    BASE_INFO_FUN(urlString);

    id body = [_opInfo objectForKey:@"body"];
    NSURL *url = [NSURL URLWithString:urlString];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    if (body != nil) {
        [request setHTTPMethod:HTTPPOST];
        if ([body isKindOfClass:[NSString class]]) {
            [request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
        }else{
            [request setHTTPBody:body];
        }
    }else{
        [request setHTTPMethod:HTTPGET];
    }

    return request;

}

//使用delegate异步发送请求
- (void)executeOp
{
    _connection = [[NSURLConnection alloc] initWithRequest:[self urlRequest] delegate:self];
}


- (void)parseData:(NSData *)data
{
    if (data.length == 0) {
        [self parseSuccess:nil jsonString:nil];
        return;
    }
    NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSMutableDictionary *dict = [JsonUtility jsonValueFromString:jsonString];
    NSString *result = [dict objectForKey:NetResult];
    if ([result isEqualToString:NetOk]) {
        [self parseSuccess:dict jsonString:jsonString];
    }else{
        [self parseFail:dict];
    }
    _receiveData = nil;

}
- (void)parseSuccess:(NSDictionary *)dict jsonString:(NSString *)jsonString
{
    [_delegate opSuccess:dict];
}
- (void)parseFail:(id)dict
{
    if ([dict isKindOfClass:[NSString class]]) {
        [_delegate opFail:(NSString *)dict];
        return;
    }
    if ([[dict objectForKey:NetResult] isEqualToString:NetInvalidateToken]) {
        BASE_ERROR_FUN(NetInvalidateToken);
    }
    [_delegate opFail:[dict objectForKey:NetMessage]];
}
- (void)parseProgress:(long long)receivedLength
{

}

#pragma mark NSURLConnectionDelegage methods

// 收到回应
- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)aResponse
{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)aResponse;
    NSString *statusCode = [NSString stringWithFormat:@"%ld",(long)[response statusCode]];

    _statusCode = [statusCode intValue];
    _receiveData = [[NSMutableData alloc] init];
    if (_statusCode == 200 || _statusCode == 206) {
        _totalLength = [response expectedContentLength];
    }
    BASE_INFO_FUN(statusCode);
}
// 接收数据
- (void)connection:(NSURLConnection *)aConn didReceiveData:(NSData *)data
{
    BASE_INFO_FUN(([NSString stringWithFormat:@"%lu", (unsigned long)data.length]));
    [_receiveData appendData:data];
    [self parseProgress:_receiveData.length];
}
// 数据接收完毕
- (void)connectionDidFinishLoading:(NSURLConnection *)aConn
{
    BASE_INFO_FUN([[NSString alloc] initWithData:_receiveData encoding:NSUTF8StringEncoding]);

    // 成功接受:200有数据,204没有数据,206断点续传
    if (_statusCode == 200 || _statusCode == 204 || _statusCode == 206) {
        [self parseData:_receiveData];
    }
    else {

        NSString *errorMessage = [[NSString alloc] initWithData:_receiveData encoding:NSUTF8StringEncoding];

        if (errorMessage.length <= 0) {
            errorMessage = [[NSString alloc] initWithFormat:@"ResponseCode:%ld", (long)_statusCode];
        }

        [self parseFail:errorMessage];
    }

    _connection = nil;
    _receiveData = nil;
}
// 返回错误
- (void)connection:(NSURLConnection *)aConn didFailWithError:(NSError *)error
{
    [self parseFail:[error localizedDescription]];

    _connection = nil;
    _receiveData = nil;
}

@end

在BaseOperation类中,使用了OC原生的网络请求类URL,URLRequest,URLConnection来异步请求网络,并将请求的结果封装成NSMutableDictionary传递给OpSuccess方法。在子类中,我们可以复写OpSuccess方法来接收参数完成对具体Model的封装。

ViewController基类

ViewController基类负责将ViewModel和View(xib)之间交互,完成UI界面展示,数据填充,事件响应等功能。
新建BaseController类
BaseController.h

//
//  BaseController.h
//  NewsReader
//
//  Created by 唐有欢 on 15/10/13.
//  Copyright © 2015年 tangtech. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "BaseOperation.h"
#import "Activity.h"
@interface BaseController : UIViewController<BaseOperationDelegate>{
    BaseOperation *_operation;
    Activity *_activity;
}

- (void)showIndicator:(NSString *)tipMessage
             autoHide:(BOOL)hide
           afterDelay:(BOOL)delay;
- (void)hideIndicator;

// 导航栏设置
- (void)setNavigationTitleImage:(NSString *)imageName;
- (void)setNavigationLeft:(NSString *)imageName sel:(SEL)sel;
- (void)setNavigationRight:(NSString *)imageName;
- (void)setStatusBarStyle:(UIStatusBarStyle)style;
- (void)opSuccess:(id)data;

@end

BaseController.m

#import "BaseController.h"
#import "ActivityIndicator.h"
@implementation BaseController


-(void)viewDidLoad
{
    [super viewDidLoad];
    [self setNavigationLeft:@"NavigationBell.png" sel:nil];
    [self setNavigationRight:@"NavigationSquare.png"];
    //[self setNavigationTitleImage:@"NavBarIcon.png"];
}

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self setNavBarImage];
    [[UIApplication sharedApplication] setStatusBarHidden:NO];
    [self setStatusBarStyle:UIStatusBarStyleLightContent];

}

- (Activity *)showActivityInView:(UIView *)view
{
    Activity *activity = [[ActivityIndicator alloc] initWithView:view];
    CGRect frame = view.bounds;

    activity.frame = frame;
    [view addSubview:activity];
    activity.labelText = @"";

    return activity;
}

- (void)showIndicator:(NSString *)tipMessage
             autoHide:(BOOL)hide
           afterDelay:(BOOL)delay
{
    if (_activity == nil) {
        _activity = [self showActivityInView:self.view];
    }

    if (tipMessage != nil) {
        _activity.labelText = tipMessage;
        [_activity show:NO];
    }

    if (hide && _activity.alpha>=1.0) {
        if (delay)
            [_activity hide:YES afterDelay:AnimationSecond];
        else
            [_activity hide:YES];
    }
}

- (void)hideIndicator
{
    [_activity hide:YES];
}

- (void)setNavBarImage
{
    UIImage *image = [UIImage imageNamed:[Global isSystemLowIOS7]?@"NavigationBar44.png":@"NavigationBar64.png"];
    [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
    [self.navigationController.navigationBar setTintColor:[UIColor whiteColor]];

    NSDictionary *attribute = @{
                                NSForegroundColorAttributeName:[UIColor whiteColor],
                                NSFontAttributeName:[UIFont systemFontOfSize:18]
                                };

    [self.navigationController.navigationBar setTitleTextAttributes:attribute];
}

- (UIButton *)customButton:(NSString *)imageName
                  selector:(SEL)sel
{
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];

    btn.frame = CGRectMake(0, 0, 44, 44);
    [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    [btn addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];

    return btn;
}

- (void)setNavigationTitleImage:(NSString *)imageName
{
    UIImage *image = [UIImage imageNamed:imageName];
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];

    self.navigationItem.titleView = imageView;
}

- (void)setNavigationLeft:(NSString *)imageName sel:(SEL)sel
{
    UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:[self customButton:imageName selector:sel]];

    self.navigationItem.leftBarButtonItem = item;
}

- (void)setNavigationRight:(NSString *)imageName
{
    UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:[self customButton:imageName selector:@selector(doRight:)]];

    self.navigationItem.rightBarButtonItem = item;
}

- (void)setStatusBarStyle:(UIStatusBarStyle)style
{
    [[UIApplication sharedApplication] setStatusBarStyle:style];
}

- (void)opSuccess:(id)data
{
    [self hideIndicator];
}

- (IBAction)doBack:(id)sender
{
    [self.navigationController popViewControllerAnimated:YES];
}

@end

至此,我们定义好了MVVM开发模式的基类,包括:
Model
  |
  |_ BaseInfo
ViewModel
  |
  |_BaseOperation
ViewController
  |
  |_BaseController

在后面的开发过程中就可以更加关注具体功能了。

github源码:https://github.com/tangthis/NewsReader
个人技术分享微信公众号,欢迎关注一起交流
这里写图片描述

阅读更多
换一批

没有更多推荐了,返回首页