从零开始搭建iOS项目框架

APP项目搭建

上周个人刚搭建完的项目框架,在这分享一下,主要还是采用MVC设计模式,没有使用storyboard,纯代码搭的界面。

1.新建工程

1.1 新建项目

删除文件
  • 删除无用的文件(Main.storyboard,ViewController)
  • 删除plist文件中Main storyboard file base name一项
  • appdelegate进行修改
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    

    [self.window makeKeyAndVisible];

}

1.2 创建不同的target管理工程

有时候我们需要多个版本,开发版、测试版或者线上版,或因渠道不同需要区分企业版,AppStore版等。这个时候使用target来管理就比较方便

1.2.1 生成

多target生成

1.2.2 修改名字

需要修改下名字target、scheme、info.plist三项的名字,主要用来区分下版本

注意:修改了info.plist名字需要修改新建的target的配置信息文件,让其能找到info.plist文件

1.2.3 修改下工程配置文件


就可以这样使用了:

#if DEVELOPMENT
    NSLog(@"development");
#else
    NSLog(@"release");
#endif

使用方法可参考:多个Target管理

1.3 导入CocoaPods管理依赖库

自从CocoaPods升级到1.0.1之后,为多个target导入依赖库,之前的link_with语法不能用了,使用如下方法:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, ‘7.0’

# ruby语法
# target数组 如果有新的target直接加入该数组
targetsArray = [‘MyApp’, ‘MyAppTest’]
# 循环
targetsArray.each do |t|
    target t do
        pod 'MJRefresh', '~> 3.1.12'
        pod 'Masonry', '~> 0.6.1'
        pod 'AFNetworking', '~> 3.1.0'
        pod 'MJExtension'
        pod 'MBProgressHUD'
        pod 'SDWebImage'



    end
end

使用方法可参考:CocoaPods安装教程

1.4 info.plist配置

1.4.1 开启http的访问权限

info.plist 中添加以下


http访问

1.4.2 添加应用白名单

iOS9之后分享等必须添加白名单后才可以打开相关应用。字段值为LSApplicationQueriesSchemes


应用白名单
应用白名单

1.4.3 其他

设置分享到微信、微博啊等URL Types

2.项目模块化

2.1 分类各模块

分类具体按照业务需求去归类,开发前把大体的分类号,主体的文件夹建立真实的文件夹,再把真实的文件夹拖到项目中,这样的好处是,避免了在项目中创建虚拟文件夹,然后导致各类文件都放在一块,不方便查找。
大致分类:

项目目录

2.2 导入需要用的第三方库

先使用CocoaPods导入项目中一些常用的第三方库,后续再根据需求导入,有些无法用CocoaPods导入的或者是一些需要修改库中代码的拖入到工程中Vender文件夹中

2.3 创建pch文件

这个文件用来包含全局使用的头文件,例如:
1.第三方头文件
2.全局宏定义
3.自定义的头文件包含主题、网络请求地址、全局方法、key等

使用方法参考: Pch 文件的正确使用

3 全局的宏定义

具体方法大家可下载demo看下

宏定义

3.1 全局配置参数DefineMacro

全局宏定义包含一些屏幕宽高、iOS版本、手机类型等

#define SCREEN_WIDTH   [UIScreen mainScreen].bounds.size.width
#define SCREENH_HEIGHT [UIScreen mainScreen].bounds.size.height
//随机颜色
#define KRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]

3.2 样式定义DefineTheme

主要包含样式上的定义,主题颜色、各类字体大小颜色、控件大小等

//颜色
#define TITLE_COLOR [UIColor blackColor]//标题颜色
#define TEXT_COLOR [UIColor grayColor]//正文颜色
#define TIPTEXT_COLOR UIColorFromRGB(0x888888)//提示语文本颜色
#define MAIN_GROUNDCOLOR UIColorFromRGB(0xF98B1B)//主题景色
#define BACKGROUNDCOLOR UIColorFromRGB(0xF7F7F7)//背景颜色
//字体大小
#define TITLEFONT [UIFont systemFontOfSize:18]
#define TEXTFONT [UIFont systemFontOfSize:16]
#define TIPTEXTFONT [UIFont systemFontOfSize:12]

3.3 API地址DefineRequest

访问的地址,各个接口的查询

#if DEVELOPMENT //***************开发版本*************
//****************测试环境***********
//app服务重构测试
//#define BaseURLString   @"http://www-test.baidu.com/rest/post"//beta
//#define BaseURLString @"http://docker-branch02-web-tomcat.baidu.com:8080/rest/post"//分之域名
//****************开发环境(个人服务器)************
//后台XXX
#define BaseURLString  @"http://192.168.1.175:8080/baidu/rest/post"
#else          //**************生产版本**************
#define BaseURLString @"https://www.baidu.com/rest/post"
#endif

//****************接口说明************
//获取用户信息
#define Request_type_getUserInfo @"getUserInfo"
//首页广告
#define Request_type_queryBannerByType @"queryBannerByType"

4. 初期代码编写

公共类

4.1 Category

4.1.1 Method swizzling

为Controller添加一些方法,即可省略继承baseViewController的麻烦,还可以对各工厂类进行操作

#import "UIViewController+swizzling.h"
#import <objc/runtime.h>

@implementation UIViewController (swizzling)

+ (void)load {
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
    Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewDidLoad));
  
    if (!class_addMethod([self class], @selector(swizzlingViewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
}

// 我们自己实现的方法,也就是和self的viewDidLoad方法进行交换的方法。
- (void)swizzlingViewDidLoad {
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    if(![str containsString:@"UI"]){
        NSLog(@"统计打点 : %@", self.title);
    }
    [self swizzlingViewDidLoad];
}
@end

4.1.2 appDelegate添加category

用以简化appDelegate中代码

AppDelegate+ViewController.h
- (void)setAppWindows
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    
    [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor whiteColor]}];
    
    [self.window makeKeyAndVisible];

}

- (void)setRootViewController
{
    AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    app.window.rootViewController = [[RootTabViewController alloc] init];

}
AppDelegate+AppService.h

需要处理的各类服务都可以添加在这

/**
 *  系统配置
 */
- (void)systemConfigration;

/**
 *  友盟注册
 */
- (void)registerUmeng;
/**
 *  个推注册
 */

- (void)registerGeiTui;

/**
 *  检查更新
 */
- (void)checkAppUpDataWithshowOption:(BOOL)showOption;

/**
 *  获取用户信息
 */
- (void)getUserData;

4.2 DataMange

  • 存放对文件的处理方法,文件的路径
  • 数据库的处理
  • NSUserDefault的处理

4.3 Other

建一个类存放比较杂乱的一些公共方法

/**验证该字符串是否是6-16位字母和数字组合*/
+ (BOOL)checkIsDigitalAndLetter:(NSString *)string;
/**利用正则表达式验证手机号码*/
+ (BOOL)checkTel:(NSString *)str;
/**利用正则表达式验证邮箱*/
+ (BOOL)checkEmail:(NSString *)email;

4.4 UITools

一些公共控件的封装,常用的一些控件进行封装(弹窗、广告轮播等),方便以后统一修改

4.4.1 NavigationViewController封装

@interface LWTNavigationViewController ()<UINavigationControllerDelegate>

@property (nonatomic, weak) id popDelegate;

@end

@implementation LWTNavigationViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.popDelegate = self.interactivePopGestureRecognizer.delegate;
    self.delegate = self;

    //navigationBar样式设置
    self.navigationBar.barTintColor = MAIN_GROUNDCOLOR;
    [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : WHITCOLOR, NSFontAttributeName : [UIFont boldSystemFontOfSize:18]}];
    [self.navigationBar setTintColor:WHITCOLOR];    // Do any additional setup after loading the view.
}

//解决手势失效问题
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (viewController == self.viewControllers[0]) {
        self.interactivePopGestureRecognizer.delegate = self.popDelegate;
    }else{
        self.interactivePopGestureRecognizer.delegate = nil;
    }
}

//push时隐藏tabbar
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (self.viewControllers.count > 0) {
        viewController.hidesBottomBarWhenPushed = YES;
    }
    [super pushViewController:viewController animated:animated];
}

//设置样式
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

//设置返回按钮样式
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    
    UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(backBarButtonItemAction)];
    viewController.navigationItem.backBarButtonItem = backBarButtonItem;
    
}

- (void)backBarButtonItemAction
{
    [self popViewControllerAnimated:YES];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

4.4.2 tabBarController封装

#import "RootTabViewController.h"
#import "LWTNavigationViewController.h"

# define kTabbarSelectTintColor [UIColor brownColor]
# define kTabbarNormalTintColor [UIColor blackColor]

@interface RootTabViewController ()<UITabBarControllerDelegate>

@end

@implementation RootTabViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self buildUI];
    // Do any additional setup after loading the view.
}

/**构建视图*/
- (void)buildUI{
    
    self.tabBar.translucent     = NO;
    self.tabBar.backgroundImage = [CommonMethods createImageWithColor:[UIColor clearColor]];
    self.tabBar.shadowImage     = [CommonMethods createImageWithColor:[UIColor grayColor]];
    
    NSArray * normalItems       = @[@"home_gray",@"circle_gray",@"me"];
    NSArray * selectItmes       = @[@"home_blue",@"circle_blue",@"me_blue"];
    
    NSArray * controllClass     = @[@"HomePageViewController",@"MessageViewController",@"MyViewController"];
    self.delegate               = self;
    NSArray * itemTitles        = @[@"首页",@"信息",@"我"];
    NSMutableArray * controllers = [[NSMutableArray alloc]init];
    for (int i = 0; i < normalItems.count; i++)
    {
        UIViewController * homeview =[[NSClassFromString(controllClass[i]) alloc]init];
        LWTNavigationViewController * navigation =[[LWTNavigationViewController alloc]initWithRootViewController:homeview];
        navigation.tabBarItem.image                     = [[UIImage imageNamed:normalItems[i]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
        navigation.tabBarItem.selectedImage             = [[UIImage imageNamed:selectItmes[i]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
        navigation.tabBarItem.titlePositionAdjustment   = UIOffsetMake(0,-3);
        [controllers addObject:navigation];
        
        // 设置文字的样式
        NSMutableDictionary *textAttrs                  = [NSMutableDictionary dictionary];
        textAttrs[NSForegroundColorAttributeName]       = kTabbarNormalTintColor;
        NSMutableDictionary *selectTextAttrs            = [NSMutableDictionary dictionary];
        selectTextAttrs[NSForegroundColorAttributeName] = kTabbarSelectTintColor;
        [homeview.tabBarItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
        [homeview.tabBarItem setTitleTextAttributes:selectTextAttrs forState:UIControlStateSelected];
        // 设置tabbaritem 的title
        navigation.tabBarItem.title                     = itemTitles[i];
    }
    self.viewControllers = controllers;
}
@end

4.5 第三方库的封装

4.5.1 网络框架

//基本参数设置
+ (AFHTTPSessionManager *)httpManager{
    //获取请求对象
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    // 设置请求格式
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    // 设置返回格式
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    manager.requestSerializer.timeoutInterval = RequestTimeOut;
    return manager;
}

//请求处理
+ (void)postParameters:(id)parameters
               success:(void (^)(NSDictionary *resDict))success
               failure:(void (^)(NSError *error))failure
{
    
    //    if ([[AFNetworkReachabilityManager sharedManager] networkReachabilityStatus] <= 0) {
    //        [MBProgressHUD showMessageInWindow:@"网络无连接" afterDelayHide:AfterDelayHide];
    //        return;
    //    }
    AFHTTPSessionManager *manager = [self httpManager];
    //开始请求
    [manager POST:BaseURLString
       parameters:parameters
         progress:^(NSProgress * _Nonnull uploadProgress) {
         }
          success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
              NSMutableDictionary *resDict = (NSMutableDictionary *)[responseObject mj_JSONObject];
//              NSDictionary *dict = [HttpManager checkResultVaild:resDict withFunction:parameters[@"function"]];
              
             //这里可以处理数据 针对接口返回的特定参数进行处理,如单点登录等需求
              NSLog(@"\n接口:%@\n返回数据:%@",parameters[@"function"],[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
              
              success(resDict);
          }
          failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
              failure(error);
              NSLog(@"\n当前接口:%@\n错误信息:%@",parameters[@"function"],error);
          }];
}

4.5.2 其他第三方

UM、HUD等很多第三方框架,都需要进行封装,针对自己app需求进行配置,使用起来就比较方便。

Demo

有什么建议可以提出来,有更好的欢迎探讨,不定期更新
demo下载

参考

文中的内容有参考过以下文章,大家可以参考下
多个Target管理
CocoaPods安装教程
Pch 文件的正确使用
iOS黑魔法-Method Swizzling
手把手教你iOS项目基本框架

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容