原文地址前端
题外话:
此篇文章以一个iOS新手的角度解释一款新闻类iOS APP诞生的过程,详细介绍在这过程当中碰到的问题和个人解决思路。欢迎你们指正。android
菜单界面:git
主界面:github
详细页面:web
关于咱们页面:数据库
具体项目背景跳过,咱们着重看如何实现一款新闻类APP。安全
在开始项目计划前,我下载了大量的新闻类APP进行研究,不管是android仍是iOS,充斥着大量的此类APP,好比网易新闻,一点咨询,新浪微博(暂归位新闻类),知乎,知乎日报等,而后我从零开始点开每一个应用,思考它的流程以及布局和要点。发现核心不外乎如下两点:服务器
但是咱们的应用不可能就这个样子,须要有不一样的新闻模块,因此又有如下几点:网络
固然,有了APP后咱们的客户端从哪请求数据呢?因而有了如下问题:架构
OK,暂先咱们只想到这,至于优化本地存储和用户友好展现等,咱们后面会涉及到。
一开始咱们只是个想法,即使想了不少,依然仍是觉的不踏实,没有清晰的项目概念,因此我把项目的草图画了下来,以下:
这下内心踏实多了,起码知道本身想让它长个什么样子了。
ok,草图已经有了,那想必你们已经知道我选用了流行的侧边栏(抽屉导航)布局。你们都知道盖一栋大楼最重要的是先把整栋大楼的骨架先搭起来,一样一个APP首先要作的是把本身的架构搭起来,而这就是咱们的布局。
iOS有着众多开源的项目,这是我之前就知道的(PS:这些都是经验才能获得的,你能够经过看书,看博客,浏览官网,与大牛交流等各类渠道获取这些信息),而对于相似咱们这种侧边栏的布局,开源界有着众多的解决方案,我选用了SWRevealViewController **这套开源库,地址是:
https://github.com/John-Lluch/SWRevealViewController (fork: **440 star: 1660)
经过内部的示例,相信你们很快就能搭建好项目的基本骨架。这里有一个另外的小源码,但愿对你掌握这块开源框架提供帮助。附连接:http://mexiqq.qiniudn.com/VReaderSidebarDemo.zip
案例效果:
核心页面是一个主页面和一个详细页面:
OK,核心界面有了(PS:我这里使用的是Xcode的storboard构建页面,不使用旧式的xib)
效果以下:
终于到了这个移动开发人员头疼的问题了,因为项目须要,咱们必须有能够提供数据的接口,对于前端移动开发人员来讲若是能够本身构建后台无疑减小了大量的沟通问题,然而人的精力有限,没法同时兼顾,咋么办?
OK,如今这个问题获得了不错的解决,愈来愈多的云服务开始出现,小编今天给你们推荐一款移动端后台开发平台Bomb(固然同类型的很多,不如facebook的Parse,不过国内你们都懂的),它提供了一套后台开发所使用的SDK,方便的为你的前端产品搭建后台,适合中小企业或者独立开发者使用。
附官方连接:http://www.bmob.cn/
附个人推荐连接:
http://www.mexiqq.com/2014/09/09/Bomb%E4%BA%91%E6%9C%8D%E5%8A%A1%E4%BB%8B%E7%BB%8D%E6%8A%95%E7%A5%A8/
给出个人平台数据库图片:
如图所示个人云端数据库有7个表,User是用户的注册表,由Bomb自动帮咱们创建,由于咱们是简单的新闻资讯类APP,因此未使用。接下来有四个module表,分别对应我新闻内容的四个模块(我添加了title,publishTime,content三个字段)。GameScore暂时未用。Question用于用户提交问题反馈的表(我添加了Question 和 Reply两个字段)。
OK,对于一个简单的新闻咨询类APP这个后台我想已经足够了。
虽然这里用不着,但我说些题外话,在iOS开发中,对于web service的访问是重中之重,主要涉及如下三个方面:
上面的看起来对于初学者有些恐怖,不过好消息是Bomb提供了封装好的SDK,使用自带的BombQuery去查询数据库。现面给出个人一个例子:
(PS:在使用以前记得将应用秘钥加入项目,方法见Bomb官网)
//请求数据 BmobQuery *bquery = [BmobQuery queryWithClassName:module]; [bquery selectKeys:@[@"title",@"publishTime"]]; [bquery setLimit:15]; [bquery orderByDescending:@"createdAt"]; [bquery findObjectsInBackgroundWithBlock:^(NSArray *array, NSError *error) { if(!error){ local = false;(步骤1) myarray = array;(步骤2) [NSThread detachNewThreadSelector:@selector(saveContent:) toTarget:self withObject:array];(步骤3) [self performSelector:@selector(reloadData) withObject:nil afterDelay:0];(步骤4) }else{ [self.refreshControl endRefreshing]; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"没法获取数据,请检查网络(☆_☆)" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; } }];
上述例子查询了数据库的15条例子,采用异步访问的方式返回数据。发生错误时给出提示,若是成功,会有四步操做:
后面会详细涉及到数据持久话操做,在这里咱们没必要细究。
OK,咱们使用Bomb本身提供的查询方法访问Bomb服务,成功的将应用与服务器链接起来。
这是我访问数据后展现新闻列表的页面:
OK,其实咱们几乎不涉及业务逻辑,由于咱们只是把最新的新闻数据展现给用户看,不过仍然有一些逻辑须要咱们注意:
解决方案:
示例代码:
self.refreshControl = [[UIRefreshControl alloc] init]; self.refreshControl.backgroundColor = [UIColor lightGrayColor]; self.refreshControl.tintColor = [UIColor whiteColor]; [self.refreshControl addTarget:self action:@selector(getLatestLoans) forControlEvents:UIControlEventValueChanged];
效果图案:
(PS:以上代码和示例只是用于展现,不推荐直接使用,建议先找几篇文章系统学习如下)
在这里我推荐两篇教程,很简明的讲述了Core Data的基本使用,附连接:
http://www.appcoda.com/introduction-to-core-data/
http://www.appcoda.com/core-data-tutorial-update-delete/
再给出我处理保存动做时执行的代码:
- (IBAction)saveNews:(id)sender { NSManagedObjectContext *managedObjectContext = [self managedObjectContext]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"CollectionNews"]; NSPredicate *predicate = [NSPredicate predicateWithFormat: @"newsTitle == %@",titleName]; [fetchRequest setPredicate:predicate]; NSArray *news = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy]; if (news.count == 0) { NSManagedObject *newRecentlyNews = [NSEntityDescription insertNewObjectForEntityForName:@"CollectionNews" inManagedObjectContext:managedObjectContext]; [newRecentlyNews setValue:titleName forKey:@"newsTitle"]; [newRecentlyNews setValue:publishTime forKey:@"newsPublishTime"]; [newRecentlyNews setValue:newsContent.text forKey:@"newsContent"]; //[newRecentlyNews setValue:[obj objectForKey:@"playerName"] forKey:@"newsContent"]; NSError *error = nil; if (![managedObjectContext save:&error]) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"发生意外(☆_☆)" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; }else{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"保存成功Y(^_^)Y" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; } }else{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"重复保存哦,亲(☆_☆)" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; } }
首先查询表中有无重复的信息,若是有提示重复保存,若是没有则新建一个NSManageObject保存信息。
效果图:
- 使用Bomb提供的服务提交问题到Question表
Bomb不只给我提供了查询的API,一样提供了增删改查一整套API,因此咱们能使用Bomb提供的服务将用户的问题进行提交,给出个人代码和运行效果:
- (IBAction)submitQuestion:(id)sender { if(![_myQuestion.text isEqualToString:@"在此输入:"]){ BmobObject *gameScore = [BmobObject objectWithClassName:@"Questions"]; [gameScore setObject:_myQuestion.text forKey:@"question"]; [gameScore saveInBackgroundWithResultBlock:^(BOOL isSuccessful, NSError *error) { //进行操做 if(error ==nil){ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"提交成功Y(^_^)Y" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; }else{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"提交失败,请检查网络(☆_☆)" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; } }]; }else{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"友情提示" message:@"请输入有效文字(☆_☆)" delegate:self cancelButtonTitle:@"关闭" otherButtonTitles:nil, nil]; [alertView show]; } }
咱们本着主要功能去优化用户的体验,我想到如下两点,其实也是最基本的两点:
好伐,以上两点应该是咱们应用中用户体验感最强的两点,下面给出个人解决思路:
对于须要用户等待的时间段内我会展现一个旋转的进度条,告诉用户我在加载数据,这个进度条可使用iOS 提供的UIActivityIndicatorView类,只须要经过如下三行代码便可改善用户体验:
[myprogress setHidesWhenStopped:YES];//设置进度条中止时隐藏 [myprogress startAnimating];//设置进度条开始旋转 [myprogress stopAnimating];//设置进度条中止旋转
只须要在加载前设置进度条开始旋转,异步加载完数据后设置进度条中止旋转便可。
代码以下:
-(BOOL)isLocalData{ //NSLog(@"titlename%@",titleName); NSManagedObjectContext *managedObjectContext = [self managedObjectContext]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:myEntimty]; NSPredicate *predicate = [NSPredicate predicateWithFormat: @"newsTitle == %@",titleName]; [fetchRequest setPredicate:predicate]; NSArray *news = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy]; if(news.count > 0) { NSManagedObject *entiy = [news objectAtIndex:0]; if(![entiy valueForKey:@"newsContent"]){ return false; }else{ [newsContent setText:[entiy valueForKey:@"newsContent"]]; [self changeFrameSizeOfTextView]; return true; } } return false; }
上述代码在用户进入详细页面后执行,检测本地是否已经拥有此条新闻内容,若是已经存在,则从本地获取,若不存在,则远程获取数据。(PS:因为我本地最多存取15条新闻,每次刷新时会清空原先的数据,因此直接经过新闻标题查取,不推荐)
OK,通过以上七步,基本已经搭建起一个简单的新闻类APP了,可是咱们的应用会不会太单调了呢?咱们能够给本身的应用增长一些额外的模块,具体选择什么要视本身的状况。这里由于是给学校作了一个就业信息发布的APP,因此添加了校车查询,地图显示的富类模块。
https://github.com/mexiQQ/VReader-iOS.git
欢迎各位大牛们指点一二 Y(^_^)Y