iOS 龙的天空 (Obj-c)

联系方式


Head

  • 这个APP是我的第一个独立开发的作品,主要是为了方便我自己使用(当然也方便了别人)╮(╯_╰)╭,使用的是object-c写的。是一个论坛网站(龙的天空)的APP实现。有兴趣的可以去上面观摩一下。APP Store
  • 这篇文章主要是介绍下本APP的一些项目搭建与功能实现

About

  • 工具是用的Xcode 7.3,抓接口是用的青花瓷和chrome自带的开发者工具。PS:其实chrome调试挺好用的- 项目启动是从4月中旬开始的,然后隔三差五的花个1到2小时写写,中间停了一个月的时间,到7月底结束,满打满算的话,大概真正花了一个半月的时间吧(其实主要有一些时间花在找图标,抓接口上了),期间推翻界面重新设计了一次界面,浪费了一些时间。

先来几张效果图吧

  • 首页
    首页.gif
  • 板块
    板块.gif
  • 热门
    热门.gif
  • 消息
    消息.gif
  • 用户
    用户.gif
  • 帖子内容
    帖子.gif
  • 用户交互
    用户交互.gif

1.首页
首页.png

  • 布局使用的UITableView,自定义UITableViewCell样式。
  • 实现如图所示cell距离上下左右间隔样式,只需在自定义cell的类里重写setFrame方法。如下所示:
- (void)setFrame:(CGRect)frame{ 
static CGFloat margin = 10;  
frame.origin.x = margin;    
frame.size.width -= 2 * frame.origin.x;  
frame.origin.y += margin;  
frame.size.height -= margin;   
[super setFrame:frame];}

  • 若要实现点击cell时的背景也与cell样式相同,如图:
    点击cell.png

    可以自定义cell的selectedBackgroundView,代码如下:
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(10, 0, self.frame.size.width-20, self.frame.size.height)];
view.backgroundColor = [UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1.00];
view.layer.cornerRadius = 15;
view.layer.masksToBounds = YES;
self.selectedBackgroundView = view;

2.几种cell的自定义

A.png

B.png
  • 主要分这两种,其余的都很简单。
  • A图主要利用了UILableattributedText属性,同时将网络请求回的带HTML标签的字符串通过NSHTMLTextDocumentType转换即可,无需手动截取匹配标签,省去了很多麻烦。代码如下:

string = [NSString stringWithFormat:@"<head><style>img{max-width:14;max-height:14}</style></head>%@",string];
NSMutableAttributedString * messageAttrStr = [[NSMutableAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} documentAttributes:nil error:nil]; ```

  • B图则在cell中放置了几个UILable,手动截取字符串再赋值给UILable.text即可

Time.png
  • cell中的时间显示如上图
  • 将返回的时间戳(类似于1471934323这种格式)进行相应的格式化即可,代码如下,并做好了相应的注释:
-(void)setTimeText:(NSInteger)dataLine{   
 NSString * timeString;  
  NSDate * todayDate = [NSDate date];//当前时间    
NSDate * pastDate = [NSDate dateWithTimeIntervalSince1970:dataLine];//发帖时间
    NSDateFormatter * format = [[NSDateFormatter alloc] init];  
  NSCalendar * calendar = [NSCalendar currentCalendar]; 
   NSInteger unit =  NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; 
   NSDateComponents * comDate =  [calendar components:unit fromDate:pastDate toDate:todayDate options:NSCalendarWrapComponents];//时间差值的组件
  if (comDate.year < 1) {//判断是否今年   
        if ([calendar isDateInToday:pastDate]) {//判断是否今天   
           if (comDate.hour >= 1) {//判断是否大于1小时 
               format.dateFormat = @"今天HH:mm";             
               timeString = [format stringFromDate:pastDate];      
            }else if(comDate.hour < 1 && comDate.minute >= 1){            //判断是否小于1小时     
               timeString = [NSString stringWithFormat:@"%2ld分钟前",(long)comDate.minute];      
            }else if(comDate.hour < 1 && comDate.minute < 1 && comDate.second >= 0){//判断是否在一分钟内
                timeString =@"刚刚";         
             }     
        }else if ([calendar isDateInYesterday:pastDate]){  //判断是否昨天      
          format.dateFormat = @"昨天HH:mm";       
           timeString = [format stringFromDate:pastDate];     
        }else{ // 前天之前       
          format.dateFormat = @"MM-dd HH:mm";       
          timeString = [format stringFromDate:pastDate];     
         }  
  }else{ 
       format.dateFormat = @"yyyy-MM-dd HH:mm";
       timeString = [format stringFromDate:pastDate];  
  }  
  _time.text = timeString;
}

3.消息界面的搭建

消息.png
  • 主要分为上下两个部分,上面可以点击,若按钮超出范围可以滚动,下面可以滑动切换
  • 紫色部分利用UIScrollView来搭建,红色部分利用UICollectionView来搭建。
  • 紫色部分按钮的文字需要在viewWillAppear:方法里实现,否则可能会出现相应视图未布局完成,文字不显示。
  • 红色部分设置UICollectionViewFlowLayout布局与红色大小相同,水平滚动,间距为0即可
  • 当点击按钮时会将前一个选中状态的按钮取消,再给当前按钮添加选中状态,然后将UICollectionView对象的contentOffset给设置成按钮对应的tag乘以屏幕宽度。
  • 若是滑动红色界面,则在UICollectionView对象滑动完成后的scrollViewDidEndDecelerating:里调用设置按钮的方法即可。

4.板块的业务逻辑实现

板块.png
  • 这个界面的UI搭建不难,主要是内部数据的请求处理比较繁琐- 首先要请求我的已关注列表,再请求所有板块列表,最后请求板块的信息数据,网络请求使用的是AFNetworking框架。
  • AFNetworking内部请求是异步操作,所以需要解决请求完所有的数据再刷新的问题,但所有数据请求完,需要消耗一定的时间,会给人有点慢的感觉,因为内部需要做循环截取数据的操作。所以使用了数据归档来缓解这个问题,只需要第一次请求的时候是全部请求,以后只要不清空缓存,就只需要请求关注列表和信息列表,同时,因为有了板块列表数据,即使网络不好时刷新数据,也不会显示空白,让人等的心急。

5.搜索界面

搜索.png
  • 分别是3个UIViewUIView的紫色部分是UILable,绿色部分是UIButton
  • 当搜索条文字改变就会实时调用按钮的setTitle: forState来改变文字,不能使用titleLabel.text来修改,无效!

5.1搜索帖子列表

Paste_Image.png
  • 搜索的文字会标红,使用的是NSMutableAttributedString来处理字符串,将相应的字符的位置利用Key:NSForegroundColorAttributeName来设置颜色,再将富文本整体赋值给UILable的attributedText即可

6.发帖回帖

  1. 发帖


    发帖.png
  2. 回帖


    回帖.png
  • 回帖是将发帖的标题栏给隐藏了,再设置相应的自动约束即可
  • 右上角放大缩小按钮会根据当前窗口的大小判断来决定是否缩放,利用的是获取键盘的高度的系统通知方法来控制。以下为代码范例:
 #pragma mark - 当键盘出现或改变时调用
- (void)keyboardDidShow:(NSNotification *)aNotification{ 
   _keyHidden=NO;  
  NSDictionary *userInfo = [aNotification userInfo]; 
  NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]; 
  CGRect keyboardRect = [aValue CGRectValue];  
  CGFloat height = keyboardRect.size.height;  
  CGFloat y  = keyboardRect.origin.y;  
  _keyHeight = height; 
  _keyY = y;  
  if (self.post==nil) {    
    return; 
  } 
  if (self.post.message.isFirstResponder==YES||self.post.title.isFirstResponder==YES) {    
    if (self.post.frame.origin.y==0) {    
        [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{  
              self.post.frame=CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-self.keyHeight-topHeight);   
         } completion:nil];        
     }else{       
         CGFloat maxY =  self.post.frame.size.height+topHeight+self.post.frame.origin.y;  
          if (maxY>_keyY) {        
        [UIView animateWithDuration:0.1 animations:^{   
               CGRect ract = self.post.frame;    
               ract.origin.y=[UIScreen mainScreen].bounds.size.height-topHeight-(self.post.frame.size.height+_keyHeight); 
               self.postY = ract.origin.y;       
               self.post.frame = ract;        
        }];         
         }else{         
              self.postY = self.post.frame.origin.y;     
         }       
     } 
   }
}

  • 发帖回帖的内容视图是UITextView,装载的是富文本内容。当点击图上按钮添加链接
    Paste_Image.png
  • 可以添加链接:
    QQ20160823-0@2x.png
  • 然后会在发帖框内显示:
    Paste_Image.png
  • 这里有个小的注意点,当添加了连接后,你直接在链接后面输入文字,会将文字颜色和属性更改成来链接文字的属性,如图:
    Paste_Image.png
  • 所以需要在链接文字的前后各加一个空格,同时设置空格的属性为颜色黑色,字体大小等即可。代码如下所示:
NSString * nullString = @" ";
urlText = [nullString stringByAppendingString:urlText];
urlText = [urlText stringByAppendingString:@" "];
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:urlText];
UITextPosition * beginning = weakSelf.post.message.beginningOfDocument;
UITextRange* selectedRange = weakSelf.post.message.selectedTextRange;
UITextPosition* selectionStart = selectedRange.start;
UITextPosition* selectionEnd = selectedRange.end;
NSInteger location = [weakSelf.post.message offsetFromPosition:beginning toPosition:selectionStart];
NSInteger length = [weakSelf.post.message offsetFromPosition:selectionStart toPosition:selectionEnd];
NSRange range = NSMakeRange(location, length);[weakSelf.post.message.textStorage insertAttributedString:attributeString atIndex:range.location];
[weakSelf.post.message.textStorage addAttributes:@{NSForegroundColorAttributeName:[UIColor colorWithRed:1.00 green:0.42 blue:0.42 alpha:1.00], NSLinkAttributeName:[NSURL URLWithString:URL],NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(range.location+1, urlText.length-2)];
[weakSelf.post.message.textStorage addAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(range.location+urlText.length-1, 1)];
[weakSelf.post.message.textStorage addAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(range.location, 1)];weakSelf.post.message.selectedRange=NSMakeRange(weakSelf.post.message.text.length,0);
  • PS:这里我是在Block回调里使用的,所以使用了__weak 类名 * weakSelf = self;来设置weakSelf关键字。

7.用户界面
用户界面.png

  • 这里分成了3块来布局
    • 蓝色部分
    • 又分成3块来布局:
      Paste_Image.png
    • 当红色区块无内容时会隐藏
      hide.png
  • 黄色部分为5个自定义的UIButton,点击会跳转到对应的控制器
  • 控制器与消息界面实现方式相同,共用一套视图,可以点击按钮或滑动界面
    Paste_Image.png
  • 紫色部分在无内容时也会隐藏
    Paste_Image.png
    Paste_Image.png

8.设置界面
Paste_Image.png

  • 设置界面最简单,只需要固定的几个cell就可以。
  • 这里介绍下清除缓存的功能实现
    • 首先是如何显示缓存大小
    • 需要获取缓存文件夹内容的大小,这里需要注意的是要遍历缓存文件夹内所有的文件来累加得到总大小,不能直接获取文件夹大小,直接获取到的文件夹大小与真实大小不同(当然用户不知道,要是偷懒也可以)。代码如下:
NSString  *Path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- (NSInteger)getSizeOfPath:(NSString *)Path {
    NSFileManager *mgr = [NSFileManager defaultManager];
    NSArray *subpaths = [mgr subpathsAtPath:Path];
    NSInteger totalSize = 0;
    for (NSString *subPath in subpaths) {  
      NSString *filePath = [directoryPath stringByAppendingPathComponent:subPath];
        NSInteger fileSize = (long)[[mgr attributesOfItemAtPath:filePath error:nil] fileSize];  
      totalSize += fileSize;  
  }  
  return totalSize;
}
  • 然后是删除缓存,只要直接将缓存文件删除即可,代码如下:
NSString  *Path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- (void)removePath:(NSString *)Path { 
   NSFileManager *mgr = [NSFileManager defaultManager]; 
   NSArray *subPaths = [mgr contentsOfDirectoryAtPath:Path error:nil]; 
   for (NSString *subPath in subPaths) {   
     NSString *filePath = [directoryPath stringByAppendingPathComponent:subPath];   
     [mgr removeItemAtPath:filePath error:nil];  
  }
}

9.具体帖子内容界面
Paste_Image.png

9.1控件布局

  • 上部绿框点击图所示可跳出UIPickerView来选择页面
    Paste_Image.png
    Paste_Image.png
  • 下部紫框为UIView,分别放置了几个按钮
  • 功能如图所示,一目了然,同时View 会根据当前界面的位置来自动隐藏或显示。
    Paste_Image.png

9.2内容布局
Paste_Image.png

  • 内容界面使用的是WKWebview,将HTML字符串截取合并后重新转换成HTML格式字符串再交给WKWebview处理,同时将自定义的CSS文件一并赋值,即可控制内容样式。
  • 界面里的超链接或者头像,都是通过截取WKWebview的代理方法
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler ;

里的url链接来进行判断处理。

  • 有图片的界面,可以点击图片进入图片查看界面
    Paste_Image.png
    Paste_Image.png
  • 图片有各种手势,缩放,拖移,长按,短按等
  • 这里我本来是自己实现的缩放、拖移手势,但不太完美,所以就用了一个别人写的图片手势的代码修改而来

VIPhotoViewDemo


10.其他

1.点击Tabbar按钮实现滑动到顶部或刷新

Paste_Image.png
首先是在自定义的UITabBarController里的UITabBarControllerDelegate的方法

-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;```
里面来判断当前点击的是哪个按钮对应的控制器,然后根据控制器的`view`的`contentOffset`来判断是否已经在顶部,不在则滑动到顶部,在则执行刷新操作。

---
3.框架使用
- 网络请求  [AFNetworking](https://github.com/AFNetworking/AFNetworking)
- 图片下载  [SDWebImage](https://github.com/rs/SDWebImage)
- 数据解析  [MJExtension](https://github.com/CoderMJLee/MJExtension)
- HUD显示 [SVProgressHUD](https://github.com/SVProgressHUD/SVProgressHUD)
- 刷      新   [MJRefresh](https://github.com/CoderMJLee/MJRefresh)
- 崩溃日志  [Bugly](https://github.com/BuglyDevTeam/Bugly-iOS)
- 然后框架导入使用的CocoaPods,不过最近Carthage比较流行,最近也在学习Carthage。
---
4.这是我的第一个正式且上架的APP,所以也一直在更新中,主要是在iPhone手机上使用,所以在iPad上显示时会出现有些布局样式不一样,因为没有进行iPad专门的适配,都是交给系统来做的。然后因为第一个嘛,所以怕不熟练,用的是最熟的OC写的,不过接下来的几个APP已经转swift了,swift写起来还是很舒服的。

---
## 11.最后因为我当初代码是直接提交到国内的Coding上去的,还是私有库,同时因为懒癌,所以就没有再上传到github上,所以大家是看不到代码了(话说代码我写的有些乱,也没多少注释。)

推荐阅读更多精彩内容

  • 1.badgeVaule气泡提示 2.git终端命令方法> pwd查看全部 >cd>ls >之后桌面找到文件夹内容...
    i得深刻方得S阅读 2,703评论 0 5
  • 因为要结局swift3.0中引用snapKit的问题,看到一篇介绍Xcode8,swift3变化的文章,觉得很详细...
    uniapp阅读 2,826评论 1 6
  • 一 、使用git管理工程 1、可以使用OSChina远程管理工程(免费) 2、可以使用GitHub网站进行远程管理...
    YuGHo阅读 506评论 1 3
  • 趁午休,做个导图回顾周六上了一整天正面管教的课程。 在分享之前,我想所有的关系都是指向自己,所以回来觉察与爱护、调...
    Jessica_LHM阅读 43评论 0 0
  • 我想这是对父母说的话。 “你们的孩子都不是你们的孩子,乃是生命为自己所渴望的儿女,他们借你而来却不因你而来。他们虽...
    简邱阅读 497评论 0 0