前言:
作为一名学生, 作为一名iOS开发学习者, 我个人浏览信息包括博客, 更多的选择移动终端。然而,
csdn并没有现成的客户端(不过有个web版的)。
之前曾经看到一款开源的导出工具, 但是它是基于Windows平台的。导出的也仅仅是PDF格式。而且,
对于文章的导出, 需要精确URL。无法做到边浏览别导出。
另外, 我想实现的是, 可以在没有网络的情况下, 浏览自己收藏的文章。并且,
对于自己收藏的文章, 可以分类管理。
最关键的是, 对于自己的文章, 可以做一个备份。我曾经遇到过这样一件事,
csdn账号密码泄漏, 使得他人登录我账号发表垃圾博文, 导致我的博客被封一天。那时候, 我的博客记录了170篇自己的学习点滴,
没有备份, 可以想像那时候我有多慌。可见, 备份自己文章的重要性。
基于以上种种原因, 这款基于iOS平台的博客导出工具应运而生。
具体功能:
1.支持在线浏览csdn博客
2.可导类型包括: 单篇文章, 专栏, 指定作者全部文章
3.导出方式包括: (1)导出单前浏览博文/专家/专栏 (2)导出指定URL博文/专家/专栏
4.导出文章分类管理
5.导出文章查询功能
6.导出文章自动排版, 图片自适应, 可放缩
运行效果:
看到这里, 如果你只是想体验一下这个应用, 想看看源码, 那可以从我的Github中下载。另外,
第一版已经上传到App Store上去了。 正在等待审核。
如果你想了解下整体开发过程, 欢迎继续往下看。推荐下载了源码, 对照着看。
你将学到:
1.自定义启动动画
2.网络环境判断(是否是Wi-Fi状态)
3.IOS与JavaScript的交互
4.自定义HUD加载效果(非传统菊花)
5.UITableView列表基本操作
6.列表关键字模糊查询
7.图片基本操作
下面逐一进行分析。
(一) 自定义启动动画
不同与传统的修改LaunchImage来加载一个静态的图片作为我们的欢迎界面,
我这里简单的实现了图片缩放, 文字渐渐显示的效果。
相对来说, 更加美观, 我们能做的操作也更加多。
打开AppDelegate.h文件, 声明一个变量, 用于显示我们的视图。
@property (strong, nonatomic) UIImageView *splashView; |
打开AppDelegate.m文件, 加入具体实现过程。
1.添加启动动画
//添加启动动画 [self.window makeKeyAndVisible]; splashView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 320, Screen_height)]; if ([[UIScreen mainScreen] bounds].size.height == 568) { [splashView setImage:[UIImage imageNamed:@"bgBlog-568"]]; } else { [splashView setImage:[UIImage imageNamed:@"bgBlog"]]; } [self.window addSubview:splashView]; [self.window bringSubviewToFront:splashView]; [self performSelector:@selector(scale) withObject:nil afterDelay:0.0f]; [self performSelector:@selector(showWord) withObject:nil afterDelay:2.5f]; |
2.动画具体实现
-(void)scale { UIImageView *logo_ = [[UIImageView alloc]initWithFrame:CGRectMake(119, 88, 82, 82)]; logo_.image = [UIImage imageNamed:@"csdnLogo"]; [splashView addSubview:logo_]; [self setAnimation:logo_]; } -(void)setAnimation:(UIImageView *)nowView { [UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^ { // 执行的动画code [nowView setFrame:CGRectMake(nowView.frame.origin.x- nowView.frame.size.width*0.1,
nowView.frame.origin.y-nowView.frame.size.height*0.1, nowView.frame.size.width*1.2, nowView.frame.size.height*1.2)]; } completion:^(BOOL finished) { // 完成后执行code [nowView removeFromSuperview]; } ]; } -(void)showWord { UIImageView *word_ = [[UIImageView alloc]initWithFrame:CGRectMake(75, Screen_height-100, 170, 29)]; word_.image = [UIImage imageNamed:@"word_"]; [splashView addSubview:word_]; word_.alpha = 0.0; [UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^ { word_.alpha = 1.0; } completion:^(BOOL finished) { // 完成后执行code [NSThread sleepForTimeInterval:1.0f]; [splashView removeFromSuperview]; } ]; } |
(二)网络环境判断(是否是Wi-Fi状态)
这个需要导入Wi-Fi文件夹下的Reachability.h / .m文件。
这是从苹果官方下载的。一个用来判断网络环境的文件。
我们之所以要判断是否在Wi-Fi环境下, 是因为导出文章可能使用的流量较大,
我们需要提示用户开启Wi-Fi来下载。
1.导入Reachability.h / .m文件
2.添加头文件
#import "Reachability.h" //Wi-Fi |
3.判断网络环境
if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus == ReachableViaWiFi) { //添加你想做的事情 }
|
(三)IOS与JavaScript的交互
UIWebView是iOS最常用的SDK之一,它有一个stringByEvaluatingJavaScriptFromString方法可以将javascript嵌入页面中,通过这个方法我们可以在iOS中与UIWebView中的网页元素交互。
常见的几种使用途径:
1、获取当前页面的url。
- (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"]; } |
2、获取页面title。
- (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"]; NSString *title = [webview stringByEvaluatingJavaScriptFromString:@"document.title"]; } |
3、修改界面元素的值。
NSString *js_result = [webView stringByEvaluatingJavaScriptFromString:
@"document.getElementsByName('q')[0].value='Colin';"]; |
4、表单提交:
NSString *js_result2 = [webView stringByEvaluatingJavaScriptFromString:@"document.forms[0].submit(); "]; |
当然, 还有更多的功能, 需要你自己去学习。
我们这个工具所要做的是, 获取到博文的主要内容及其标题。
以导出单篇为例。
我们打开一个博文, 显示页面源代码, 可以看到下面这样的内容。
其中的"article_content" 就对应着csdn博文的正文内容.
这是我们导出过程中需要的。(你肯定也不希望导出的文章里面嵌套着广告吧...)
所以, 我们可以通过简单的两行代码获取我们需要的内容:
//获取详细内容 NSString *lJs = @"document.getElementById(\"article_content\").innerHTML"; NSString *lHtml1 = [webView stringByEvaluatingJavaScriptFromString:lJs]; |
同理, 文章标题可以这样获得:
//获取标题 NSString *lJs2 = @"document.getElementById(\"article_details\")
.getElementsByClassName(\"article_title\")[0].getElementsByTagName(\"a\")[0].innerText"; NSString *lHtml2 = [webView stringByEvaluatingJavaScriptFromString:lJs2]; |
再深入一点, 我们甚至可以修改显示网页的图片大小
//修改图片大小 if ([lHtml1 rangeOfString:@"<img"].location != NSNotFound) { NSScanner *myScanner = [NSScanner scannerWithString:lHtml1]; NSString *myText = nil; while ([myScanner isAtEnd] == NO) { [myScanner scanUpToString:@"<img" intoString:nil]; [myScanner scanUpToString:@"s" intoString:&myText]; lHtml1 = [lHtml1 stringByReplacingOccurrencesOfString:[NSString stringWithFormat:
@"%@s",myText] withString:@"<img style=\"width:300px;height:this.offsetHeight;\" s"]; } } |
(四)自定义HUD加载效果(非传统菊花)
iOS自带的加载效果是个转圈的菊花...想必大家都清楚了。
不过说实话, 那玩意确实够难看的。
选择我们要做的是, 显示一个不封闭的圈, 并且背景视图暗一点, 突出加载过程。
效果如下:
1.导入文件夹SVProgressHUD
2.添加头文件
#import "SVProgressHUD.h" //HUD 加载显示 |
3.显示HUD
//显示HUD [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient]; |
4.移除HUD
//移除HUD [SVProgressHUD dismiss]; |
(五)UITableView列表基本操作
这个是基础内容了, 具体可以看blogExportedList.m这里.
有详细讲解。
这里着重介绍下左滑删除操作。
1.开启运行删除模式
2.响应删除的时候, 需要做到 (1) 从列表中移除 (2)从数据中移除
(3)刷新列表
//删除 -(UITableViewCellEditingStyle)tableView:(UITableView *)
tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewCellEditingStyleDelete; } /*改变删除按钮的title*/ -(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:
(NSIndexPath *)indexPath { return @"删除"; } /*删除用到的函数*/ -(void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { //获取完整路径 以及字典和数组的初始化 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString * namePath = [documentsDirectory stringByAppendingPathComponent:@"csdnInfo.plist"]; [blogArr removeObjectAtIndex:indexPath.row]; [blogArr writeToFile:namePath atomically:YES]; [nameList removeObjectAtIndex:indexPath.row]; [myTableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic]; //删除对应数据的cell } } |
(六)列表关键字模糊查询
搜索的话, 是个很常用的功能。如果简单的关键字搜索, 想必大家都会.
但是我们需要的是模糊查询。 比如要查询 "iOS开发"
当你输入i, o开, 等等, 都能找到对应的数据。
1.导入查询字列表文件夹
2.添加头文件
//查询子列表 #import "DDList.h" #import "PassValueDelegate.h" |
3.初始化查询列表
//查询列表初始化 nameList = [[NSMutableArray alloc]init]; for (int i =0; i<[blogArr count]; i++) { [nameList addObject:[[blogArr objectAtIndex:i]objectForKey:@"name"]]; } //初始化查询的字符串 _searchStr = @""; //初始化提醒视图 _ddList = [[DDList alloc] initWithStyle:UITableViewStylePlain]; _ddList._delegate = self; [self.view addSubview:_ddList.view]; [_ddList.view setFrame:CGRectMake(30, 108, 200, 0)]; _ddList._totalList = nameList; |
4.响应查询
具体可以看代码, 这里强调一个。 当查到数据点击的时候, 我们需要自动跳转到指定的位置。
不然查询也没用了..
//隐藏提醒视图 - (void)setDDListHidden:(BOOL)hidden { NSInteger height = hidden ? 0 : 180; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:.2]; [_ddList.view setFrame:CGRectMake(30, 108, 200, height)]; [UIView commitAnimations]; } //单例,传回选中提醒框中的结果 #pragma mark - #pragma mark 传回数据 - (void)passValue:(NSString *)value { //如果有选中,则修改当前搜索内容为返回结果,调用结束函数searchBarSearchButtonClicked if (value) { _searchBar.text = value; [self searchBarSearchButtonClicked:_searchBar]; } else { } } //搜索框中的字符改变时候调用 #pragma mark - #pragma mark SearchBar Delegate Methods - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { //如果搜索框有内容,更新提示列表 if ([searchText length] != 0) { _ddList._searchText = searchText; [_ddList updateData]; [self setDDListHidden:NO]; } else { [self setDDListHidden:YES]; //否则隐藏 } } //文本框弹出,开始搜索 - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { searchBar.showsCancelButton = YES; for(id cc in [searchBar subviews]) { if([cc isKindOfClass:[UIButton class]]) { UIButton *btn = (UIButton *)cc; [btn setTitle:@"取消" forState:UIControlStateNormal]; } } return YES; } //开始搜索响应。 - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { searchBar.text = @""; } //结束文本框输入 - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { searchBar.showsCancelButton = NO; searchBar.text = @""; } //当选中了提示列表中的某个,搜索结束,选中结果,并且高亮 - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self setDDListHidden:YES]; //隐藏提示视图 _searchStr = [searchBar text]; //获得查询结果 [searchBar resignFirstResponder]; //收回键盘 for (int i = 0; i<[blogArr count]; i++) //从列表中查找结果,选中 { if ([[[blogArr objectAtIndex:i]objectForKey:@"name"] isEqualToString:_searchStr]) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; [myTableView reloadData]; [myTableView scrollToRowAtIndexPath:indexPath //滚动视图 atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; [myTableView selectRowAtIndexPath:indexPath //选中高亮 animated:YES scrollPosition:UITableViewScrollPositionMiddle]; } } } //取消搜索响应 - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [self setDDListHidden:YES]; [searchBar resignFirstResponder]; } |
(七)图片基本操作
这里我们需要做的是, 点击一个图片, 跳转到另外一个视图, 这里视图允许对图片进行常规编辑。包括图片的缩放...
具体可以看MRZoomScrollView.h / .m这两个文件。
下面解释下实现方法。
1.添加手势。
// Add gesture,double tap zoom imageView. UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; [doubleTapGesture setNumberOfTapsRequired:2]; [imageView addGestureRecognizer:doubleTapGesture]; |
我们的放缩都是通过手势识别来实现的。 比如, 你双指拖拉, 就是放缩。 双指点击是自动放缩, 单指点击是退出图片浏览。
2.响应手势操作
#pragma mark - Zoom methods - (void)handleDoubleTap:(UIGestureRecognizer *)gesture { float newScale = self.zoomScale * 1.5; CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gesture locationInView:gesture.view]]; [self zoomToRect:zoomRect animated:YES]; } - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center { CGRect zoomRect; zoomRect.size.height = self.frame.size.height / scale; zoomRect.size.width = self.frame.size.width / scale; zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0); zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0); return zoomRect; } |
至此, 这篇简单的教程就结束了。希望, 能给您带来一点收获。
|