app开发总结 4 - ios总结

一、项目搭建

  1. 框架:对于app没有特别复杂的业务逻辑,核心为界面展示、数据交互,因此采用两层结构 + MVC的模式实现,对于实体的分类,可分为resq、resp,分别对应请求、响应;
    这里写图片描述

  2. 项目结构如下:
    1,library:主要是一些第三方的库,如支付宝、微信、sharesdk、Reachability… 第三方库建议优先使用CocoaPods
    2,resource:资源文件,例如html、css、音频等
    3,util:工具类,如http、json、MD5、sqlite、base64、3des、rsa等…
    4,config:配置文件,例如全局的常量配置、url配置、通用宏定义…
    5,application:主要是AppDelegate
    6,core:应用的核心部分,下面代表跟业务相关的各个独立模块,

    这里写图片描述

  3. core说明:此模块为应用的核心,包含了所有的业务相关模块,主要包括

    • controller:控制器,负责接收界面事件、完成交互、处理逻辑,并实现Model与View的分离
    • model:数据实体,分为resq、resp,分别对应请求、响应
    • view:视图
    • datalayer:数据层,又分为接口层、本地数据层
      这里写图片描述

二、UI层设计

BaseViewController的基类封装:
1,viewDidLoad:通常会在视图加载时,完成三个操作,即初始化成员变量、初始化视图、加载数据,因此可在基类定义函数,交由子类实现


/**
 *  所有视图控制器的基类
 */
@interface BaseViewController : UIViewController

/*
 * 初始化变量
 */
-(void) initVariable;

/*
 * 初始化视图
 */
-(void) initViews;

/*
 * 加载数据
 */
-(void) loadData;

/*
 * 返回处理
 */
-(void) back;

/*
 * 返回到指定指定界面
 */
-(void) backToAssignedViewController:(Class) clazz;

@end
#import "BaseViewController.h"

@implementation BaseViewController

#pragma mark - 初始化操作
/*
 * 视图加载
 */
- (void)viewDidLoad {
    [super viewDidLoad];

    //开启ios右滑返回
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.delegate = nil;
    }

}

/*
 * 初始化变量
 */
-(void) initVariable{

}

/*
 * 初始化视图
 */
-(void) initViews{

}

/*
 * 加载数据
 */
-(void) loadData{

}
/*
 * 返回函数,可由子类重写
 */
-(void) back{
    [self.navigationController popViewControllerAnimated:YES];
}

/*
 * 跳转到指定界面
 */
-(void) backToAssignedViewController:(Class) clazz{

    for(UIViewController *controller in self.navigationController.viewControllers){

        if([controller isKindOfClass:clazz]){
            [self.navigationController popToViewController:controller animated:YES];
            break;
        }

    }

}
@end

2,导航栏可通过Category实现

#import <UIKit/UIKit.h>

@interface UIViewController (Navigation)

/*
 * 设置导航栏样式 - 透明
 */
-(void) setNavigationBarTransparent;

/*
 * 设置导航栏样式 - 默认
 */
-(void) setNavigationBarDefaultStyle;

/*
 * 初始化导航栏返回按钮
 */
-(void) initNavigationBarBackButton:(NSString *) backStr;


@end


#import "UIViewController+Navigation.h"
#import "UIColor+Convert.h"
#import "TextSizeUtil.h"

@implementation UIViewController (Navigation)


/*
 * 设置导航栏样式 - 透明
 */
-(void) setNavigationBarTransparent{

    //1,状态栏高亮(相当于透明,跟随导航栏)
    [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

    //2,导航栏透明
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"BgTransparent"] forBarMetrics:UIBarMetricsDefault];
    self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
    self.navigationController.navigationBar.translucent = YES;
    [self.navigationController.navigationBar setShadowImage:[[UIImage alloc] init]];        //去除分割线

}

/*
 * 设置导航栏样式 - 非透明
 */
-(void) setNavigationBarDefaultStyle{

    //1,状态栏高亮(相当于透明,跟随导航栏)
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

    //2,导航栏颜色
    self.navigationController.navigationBar.barTintColor = [UIColor colorWithHexString:@"3A3A3F"];
    self.navigationController.navigationBar.translucent = NO;
    [self.navigationController.navigationBar setShadowImage:[[UIImage alloc] init]];
    [self.navigationController.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], NSForegroundColorAttributeName, nil]];

}


/*
 * 初始化导航栏返回按钮
 */
-(void) initNavigationBarBackButton:(NSString *) backStr{

    //自定义navigation controller的返回按钮
    UIButton *btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
    btnBack.titleLabel.textAlignment = NSTextAlignmentLeft;
    [btnBack setTitle:backStr forState:UIControlStateNormal];
    btnBack.titleLabel.font = [UIFont systemFontOfSize: 16.0];
    [btnBack setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [btnBack setTitleColor:[UIColor colorWithHexString:@"898788"] forState:UIControlStateHighlighted];
    [btnBack addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];

    //根据文字计算按钮大小
    CGRect rect = [TextSizeUtil getRectByStr:backStr:16.0 :80 :44];
    [btnBack setFrame:CGRectMake(0, 0, rect.size.width + 20,  44)];         //根据文字计算按钮宽度

    [btnBack setImage:[UIImage imageNamed:@"IconBack"] forState:UIControlStateNormal];
    [btnBack setImageEdgeInsets:UIEdgeInsetsMake(0.0, -10, 0.0, 0.0)];      //文字、图片之间间隔10


    //返回按钮边距
    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithCustomView:btnBack];
    UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]
                                       initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
                                       target:nil action:nil];
    negativeSpacer.width = -5;
    self.navigationItem.leftBarButtonItems = @[negativeSpacer, backItem];

}

@end

3,UIVIew+FrameAdjust:便于改变UIView的大小、位置

/**
 *  提供简便的方法,设置frame
 */
@interface UIView (FrameAdjust)

- (CGFloat)x;

- (void)setX:(CGFloat)x;


- (CGFloat)y;

- (void)setY:(CGFloat)y;


- (CGFloat)width;

- (void)setWidth:(CGFloat)width;


- (CGFloat)height;

- (void)setHeight:(CGFloat)height;

@end
#import "UIView+FrameAdjust.h"

@implementation UIView (FrameAdjust)


- (CGFloat)x{
    return self.frame.origin.x;
}

- (void)setX:(CGFloat)x{
    self.frame = CGRectMake(x, self.y, self.width, self.height);
}

- (CGFloat)y{
    return self.frame.origin.y;
}

- (void)setY:(CGFloat)y{
    self.frame = CGRectMake(self.x, y, self.width, self.height);
}


- (CGFloat)width{
    return self.frame.size.width;
}

- (void)setWidth:(CGFloat)width{

}

- (CGFloat)height{
    return self.frame.size.height;
}

- (void)setHeight:(CGFloat)height{
    self.frame = CGRectMake(self.x, self.y, self.width, height);
}

@end

4,适配部分:采用autolayout,即相对布局,并结合Masonry改变约束即可

5,UIView+Loading;加载框,主要分为加载对话框、加载动画、无网络提示重新加载,此处暂仿美团实现

//用于在无网络时,回调到UIViewController
typedef void (^OnClickReload) (void);


/**
 * 为UIView扩展加载框的功能,通常在UIViewController的rootView中开启,加载框分为两种
 * 1, 加载对话框:主要用于登录、注册等操作
 * 2, 加载动画:主要用于获取数据操作,
 */
@interface UIView (Loading)

/**
 *  显示加载对话框
 */
-(void) showLoadingDialog;

/**
 *  关闭加载对话框
 */
-(void) closeLoadingDialog;

/**
 *  显示自定义GIF加载动画(仿美团)
 */
-(void) showLoadingGIFAnimation;


/**
 *  关闭自定义GIF加载动画(仿美团)
 */
-(void) closeLoadingGIFAnimation;


/**
 *  显示无网络视图
 */
-(void) showNotNetworkView:(NSError *) error :(UIViewController *) controller;

/**
 *  关闭无网络视图
 */
-(void) closeNotNetworkView;

/**
 *  弹出toast提示
 */
-(void) toast:(NSString *) text;


@end


@implementation UIView (Loading)

/**
 *  显示加载对话框
 */
-(void) showLoadingDialog{
    [MBProgressHUD showHUDAddedTo:self animated:YES];
}


/**
 *  关闭加载对话框
 */
-(void) closeLoadingDialog{
    [MBProgressHUD hideHUDForView:self animated:YES];
}

/**
 *  显示自定义GIF加载动画(仿美团)
 */
-(void) showLoadingGIFAnimation{

    NSArray *nibs = [[NSBundle mainBundle] loadNibNamed:@"LoadingGIFAnimationView" owner:nil options:nil];

    LoadingGIFAnimationView *loadingView = [nibs lastObject];
    loadingView.center = self.center;
    [self addSubview:loadingView];

    [loadingView startLoadingAnimation];

}


/**
 *  关闭自定义GIF加载动画(仿美团)
 */
-(void) closeLoadingGIFAnimation{

    for (UIView *subview in self.subviews) {

        if ([subview isKindOfClass:[LoadingGIFAnimationView class]]) {
            [subview removeFromSuperview];
        }

    }

}

/**
 *  显示无网络视图(仿美团)
 */
-(void) showNotNetworkView:(NSError *) error :(UIViewController *) controller{

    NSArray *nibs = [[NSBundle mainBundle] loadNibNamed:@"NotNetworkView" owner:nil options:nil];

    NotNetworkView *loadingView = [nibs lastObject];
    loadingView.center = self.center;

    //无网络
    if(NSURLErrorNotConnectedToInternet == error.code){
        loadingView.labelReason.text = @"您的网络好像不太给力,请稍后再试";
    }
    //请求超时
    else if(NSURLErrorTimedOut == error.code){
        loadingView.labelReason.text = @"请求超时,请稍后再试";
    }
    //连接不到服务器
    else if(NSURLErrorCannotConnectToHost == error.code){
        loadingView.labelReason.text = @"无法连接到服务器,请稍后再试";
    }
    //未知错误
    else if(NSURLErrorUnknown == error.code){
        loadingView.labelReason.text = @"未知错误";
    }

    [loadingView.btnReload addTarget:controller action:@selector(notNetworkClickRefresh) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:loadingView];

}

/**
 *  关闭无网络视图(仿美团)
 */
-(void) closeNotNetworkView{

    for (UIView *subview in self.subviews) {

        if ([subview isKindOfClass:[NotNetworkView class]]) {
            [subview removeFromSuperview];
        }

    }

}

/**
 *  弹出toast提示
 */
-(void) toast:(NSString *) text{
    [self makeToast:text duration:1.0f position:CSToastPositionCenter];
}

@end

这里写图片描述

这里写图片描述

这里写图片描述

6,引导页、登录页、首页的管理(在didFinishLaunchingWithOptions中判断)

三、数据层设计

数据层:分为接口层、本地数据层,此处仅对接口层进行说明

首先是HttpUtil,提供发起http请求的工具类:对于回调的方式,可采用block、也可采用统一回调管理

#import <Foundation/Foundation.h>
#import "AFHTTPSessionManager.h"


#pragma mark http请求回调函数
/**
 *  成功的回调
 *
 *  @param responseBody 响应数据
 */
typedef void(^OnSuccess) (id responseBody);

/**
 *  失败回调
 *
 *  @param error 错误信息
 */
typedef void(^OnFailure) (NSError *error);




#pragma mark http请求的工具类
@interface HttpUtil : NSObject

@property (strong,nonatomic) AFHTTPSessionManager *httpSessionManager;

/**
 *  单例
 */
+(HttpUtil *) sharedManager;


/*
 * 判断网络连接状况
 */
+(BOOL) isConnectionAvailable;


/**
 *  http get请求 - block回调
 *
 *  @param url      请求url
 *  @param params   请求参数
 *  @param onSuccess  请求成功回调
 *  @param onFailure  请求失败回调
 */
-(void) doGet:(NSString *) url params :(id) params
            onSuccess: (OnSuccess) onSuccess
            onFailure: (OnFailure) onFailure;

/**
 *  http post请求 - block回调
 *
 *  @param url      请求url
 *  @param params   请求参数
 *  @param onSuccess  请求成功回调
 *  @param onFailure  请求失败回调
 */
-(void) doPost:(NSString *) url params :(id) params
             onSuccess: (OnSuccess) onSuccess
             onFailure: (OnFailure) onFailure;


/**
 *  http 多图片上传 - block回调
 *
 *  @param url        请求url
 *  @param formName   表单的name,服务端根据此获取数组
 *  @param imageArray 图片数组,可为NSData
 *  @param onSuccess  请求成功回调
 *  @param onFailure  请求失败回调
 */
-(void) multiUploadImage:(NSString *) url
            formName: (NSString *) formName
            imageArray: (NSMutableArray *) imageArray
            onSuccess: (OnSuccess) onSuccess
            onFailure: (OnFailure) onFailure;


@end

@implementation HttpUtil

/**
 *  避免重复创建HttpSessionManager
 */
-(id) httpSessionManager{

    if(_httpSessionManager == nil){
        _httpSessionManager = [AFHTTPSessionManager manager];
        _httpSessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
        _httpSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
        _httpSessionManager.requestSerializer.timeoutInterval = 10;
    }

    return _httpSessionManager;

}

/**
 *  单例
 */
+(HttpUtil *) sharedManager{

    static HttpUtil *sharedAccountManagerInstance = nil;

    static dispatch_once_t predicate;

    dispatch_once(&predicate, ^{
        sharedAccountManagerInstance = [[self alloc] init];
    });

    return sharedAccountManagerInstance;
}

/*
 * 判断网络连接
 */
+(BOOL) isConnectionAvailable{

    BOOL connected = YES;

    // 1.检测wifi状态
    Reachability *wifi = [Reachability reachabilityForLocalWiFi];

    // 2.检测手机是否能上网络(WIFI\3G\2.5G)
    Reachability *conn = [Reachability reachabilityForInternetConnection];

    // 3.判断网络状态,wifi
    if ([wifi currentReachabilityStatus] != NotReachable) {
        connected = YES;
    }
    else if ([conn currentReachabilityStatus] != NotReachable) {  //使用手机自带网络进行上网
        connected = YES;
    }
    else { // 没有网络
        connected = NO;
    }

    return connected;
}


/**
 *  http get请求 - block回调
 *
 *  @param url      请求url
 *  @param params   请求参数
 *  @param onSuccess  请求成功回调
 *  @param onFailure  请求失败回调
 */
-(void) doGet:(NSString *) url params :(id) params
    onSuccess: (OnSuccess) onSuccess
    onFailure: (OnFailure) onFailure{

    if([HttpUtil isConnectionAvailable]){

        //通过AFHttpSessionManager发送请求
        [self.httpSessionManager GET:url  parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSData *data = responseObject;
            NSString *jsonStr =  [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            onSuccess(jsonStr);             //成功回调
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            onFailure(error);               //失败回调
        }];

    }
    else{
        NSError *error = [NSError errorWithDomain:@""  code:NSURLErrorNotConnectedToInternet  userInfo:nil];         //无网络的回调
        onFailure(error);
    }

}


/**
 *  http post请求 - block回调
 *
 *  @param url      请求url
 *  @param params   请求参数
 *  @param onSuccess  请求成功回调
 *  @param onFailure  请求失败回调
 */
-(void) doPost:(NSString *) url params :(id) params
     onSuccess: (OnSuccess) onSuccess
     onFailure: (OnFailure) onFailure{

    if([HttpUtil isConnectionAvailable]){

        //通过AFHttpSessionManager发送请求
        [self.httpSessionManager POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSData *data = responseObject;
            NSString *jsonStr =  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            onSuccess(jsonStr);             //成功回调
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            onFailure(error);               //失败回调
        }];

    }
    else{
        NSError *error = [NSError errorWithDomain:@""  code:NSURLErrorNotConnectedToInternet  userInfo:nil];          //无网络的回调
        onFailure(error);
    }

}

/**
 *  http 多图片上传 - block回调
 *
 *  @param url        请求url
 *  @param formName   表单的name,服务端根据此获取数组
 *  @param imageArray 图片数组,可为NSData
 *  @param onSuccess  请求成功回调
 *  @param onFailure  请求失败回调
 */
-(void) multiUploadImage:(NSString *) url
                formName: (NSString *) formName
              imageArray: (NSMutableArray *) imageArray
               onSuccess: (OnSuccess) onSuccess
               onFailure: (OnFailure) onFailure{

    if([HttpUtil isConnectionAvailable]){

        //创建NSMutableURLRequest,设置multipart-formdata参数,并设置content-Type
        NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

            for(int i = 0 ; i < imageArray.count; i++){

                UIImage *image = [imageArray objectAtIndex:i];
                NSData *data = UIImageJPEGRepresentation(image, 0.5f);

                if(data != nil){
                    [formData appendPartWithFileData:data name:formName fileName:[NSString stringWithFormat:@"file_%d.jpg",i]  mimeType:@"image/jpeg" ];
                }

            }


        } error:nil];
        [request setTimeoutInterval:30];

        //创建上传任务
        NSURLSessionUploadTask *uploadTask = [self.httpSessionManager uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

            if(error){
                onFailure(error);
            }
            else{
                NSData *data = responseObject;
                NSString *jsonStr =  [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
                onSuccess(jsonStr);
            }

        }];

        //开始上传
        [uploadTask resume];

    }
    else{
        NSError *error = [NSError errorWithDomain:@""  code:NSURLErrorNotConnectedToInternet  userInfo:nil];              //无网络的回调
        onFailure(error);
    }

}

@end

接口层负责所有数据的获取:

#import "BaseApiManager.h"

/**
 *  用户相关 - api管理,主要是登录、注册
 */
@interface UserApiManager : BaseApiManager

/**
 *  密码登录
 *
 *  @param phone 手机号
 *  @param pwd   密码
 */
-(void) loginForPassword:(NSString *) phone :(NSString *) pasword
               onSuccess: (OnSuccess) onSuccess
               onFailure: (OnFailure) onFailure;


@end
#import "UserApiManager.h"
#import "Md5Util.h"

@implementation UserApiManager


/**
 *  登录操作
 *
 *  @param phone 手机号
 *  @param pwd   密码
 */
-(void) loginForPassword:(NSString *) phone :(NSString *) pasword
               onSuccess: (OnSuccess) onSuccess
               onFailure: (OnFailure) onFailure{


    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    [params setObject:phone forKey:@"phone"];
    [params setObject:[Md5Util md5_32:pasword] forKey:@"password"];

     [[HttpUtil sharedManager] doPost:@"http://120.76.102.13:8080/poli/mobile/user/loginForPassword.remote" params:params onSuccess:onSuccess onFailure:onFailure];

}

@end

对于http调用,应在离开当前界面时,取消请求
-(void) viewWillDisappear:(BOOL)animated{
//取消所有请求
[[HttpUtil sharedManager].httpSessionManager.operationQueue cancelAllOperations];

//取消单个请求
NSURLSessionDataTask *task = [HttpUtil doPost:.,…….];
[task cancel];

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值