首页 > 代码库 > iOS开发项目篇—36封装微博业务

iOS开发项目篇—36封装微博业务

iOS开发项目篇—36封装微博业务

一、简单说明

1.请求参数面向模型

2.请求结果面向模型

3.对控制器来说应该屏蔽业务细节。不让控制器关心(知道)业务细节,它只需要知道自己在做某个业务

@通过一个专门的业务处理类:处理微博业务细节

 

说明

业务:加载新的微博首页数据

实现:给新浪服务器发送一个GET请求

 

业务:加载更多的首页微博数据

实现1:给新浪服务器发送一个GET请求

实现2:去沙盒中加载以前离线缓存的微博数据

 

 二、实现

1.新建一个微博业务处理类,继承自NSObject

微博业务处理类中的代码设计:

YYStatusTool.h文件

 1 // 2 //  YYStatusTool.h 3 //  微博业务类:处理跟微博相关的一切业务,比如加载微博数据、发微博、删微博 4  5 #import <Foundation/Foundation.h> 6 #import "YYHomeStatusesParam.h" 7 #import "YYHomeStatusesResult.h" 8  9 @interface YYStatusTool : NSObject10 /**11 *  加载首页的微博数据12 *13 *  @param param   请求参数14 *  @param success 请求成功后的回调(请将请求成功后想做的事情写到这个block中)15 *  @param failure 请求失败后的回调(请将请求失败后想做的事情写到这个block中)16 */17 18 +(void)homeStatusesWithParam:(YYHomeStatusesParam *)param success:(void (^)(YYHomeStatusesResult *result))success failure:(void(^)(NSError *error))failure;19 20 @end

YYStatusTool.m文件

 1 // 2 //  YYStatusTool.m 3 // 4  5 #import "YYStatusTool.h" 6 #import "YYHttpTool.h" 7 #import "MJExtension.h" 8 @implementation YYStatusTool 9 10 +(void)homeStatusesWithParam:(YYHomeStatusesParam *)param success:(void (^)(YYHomeStatusesResult *))success failure:(void (^)(NSError *))failure11 {12     //把请求参数模型转换为字典13     NSDictionary *params=param.keyValues;14     15     [YYHttpTool get:@"https://api.weibo.com/2/statuses/home_timeline.json" params:params success:^(id responseObj) {16         if (success) {17             //把获取的数据(返回结果)由字典转换为模型18             YYHomeStatusesResult *result=[YYHomeStatusesResult objectWithKeyValues:responseObj];19             success(result);20         }21     } failure:^(NSError *error) {22         if (failure) {23             failure(error);24         }25     }];26 }27 @end

2.新建一个类(模型),封装加载首页微博数据的请求参数

查看新浪官方要求的请求参数:

该类中的代码设计:

 YYHomeStatusesParam.h

 1 // 2 //  YYHomeStatusesParam.h 3 //封装加载首页微博数据的请求参数 4  5 #import <Foundation/Foundation.h> 6  7 @interface YYHomeStatusesParam : NSObject 8 /**access_token    false    string    采用OAuth授权方式为必填参数,其他授权方式不需要此参数,OAuth授权后获得。*/ 9  @property(nonatomic,copy)NSString *access_token;10 11 /**since_id    false    int64    若指定此参数,则返回ID比since_id大的微博(即比since_id时间晚的微博),默认为0。*/12 @property(nonatomic,strong)NSNumber *since_id;13 14 /**max_id    false    int64    若指定此参数,则返回ID小于或等于max_id的微博,默认为0。*/15 @property(nonatomic,strong)NSNumber *max_id;16 17 /**count    false    int    单页返回的记录条数,最大不超过100,默认为20。*/18 @property(nonatomic,strong)NSNumber *count;19 20 //说明:这里把int数据包装成NSNumber对象,是为了防止字典中出现空值21 @end

3.新建一个类(模型),封装加载首页微博数据的返回结果

该类中的代码设计:

YYHomeStatusesResult.h文件

 1 // 2 //  YYHomeStatusesResult.h 3 //封装加载首页微博数据的返回结果 4  5 #import <Foundation/Foundation.h> 6  7 @interface YYHomeStatusesResult : NSObject 8 /**微博数组 装着YYStatus模型*/ 9 @property(nonatomic,strong)NSArray *statuses;10 /**近期的微博总数*/11 @property(nonatomic,assign)int total_number;12 @end

YYHomeStatusesResult.m文件

 1 // 2 //  YYHomeStatusesResult.m 3 // 4  5 #import "YYHomeStatusesResult.h" 6 #import "MJExtension.h" 7 #import "YYStatusModel.h" 8  9 @implementation YYHomeStatusesResult10 -(NSDictionary *)objectClassInArray11 {12     return @{@"statuses":[YYStatusModel class]};13 }14 @end

说明:这里使用了第三方框架,完成了字典转模型的操作。

4.在首页控制器代码中的设计

YYHomeTableViewController.m文件

  1 //  2 //  YYHomeTableViewController.m  3 //  4   5 #import "YYHomeTableViewController.h"  6 #import "YYOneViewController.h"  7 #import "YYTitleButton.h"  8 #import "YYPopMenu.h"  9 #import "YYAccountModel.h" 10 #import "YYAccountTool.h" 11 //#import "AFNetworking.h" 12 #import "UIImageView+WebCache.h" 13 #import "YYUserModel.h" 14 #import "YYStatusModel.h" 15 #import "MJExtension.h" 16 #import "YYloadStatusesFooter.h" 17 //#import "YYHttpTool.h" 18 #import "YYHomeStatusesParam.h" 19 #import "YYHomeStatusesResult.h" 20 #import "YYStatusTool.h" 21  22 @interface YYHomeTableViewController ()<YYPopMenuDelegate> 23 @property(nonatomic,assign)BOOL down; 24 @property(nonatomic,strong)NSMutableArray *statuses; 25 @property(nonatomic,strong)YYloadStatusesFooter *footer; 26 @property(nonatomic,strong) YYTitleButton *titleButton; 27 @end 28  29 @implementation YYHomeTableViewController 30  31 #pragma mark- 懒加载 32 -(NSMutableArray *)statuses 33 { 34     if (_statuses==nil) { 35         _statuses=[NSMutableArray array]; 36     } 37     return _statuses; 38 } 39 - (void)viewDidLoad 40 { 41     [super viewDidLoad]; 42      43     //设置导航栏内容 44     [self setupNavBar]; 45      46     //集成刷新控件 47     [self setupRefresh]; 48      49     //设置用户的昵称为标题 50     //先显示首页标题,延迟两秒之后变换成用户昵称 51     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 52         [self setupUserInfo]; 53     }); 54 } 55  56 /** 57  *设置用户的昵称为标题 58  */ 59 -(void)setupUserInfo 60 { 61 //    //1.封装请求参数 62 //    NSMutableDictionary *params=[NSMutableDictionary dictionary]; 63 //    params[@"access_token"] =[YYAccountTool accountModel].access_token; 64 //    params[@"uid"]=[YYAccountTool accountModel].uid; 65 //     66 //    //2.发送网络请求 67 //    [YYHttpTool get:@"https://api.weibo.com/2/users/show.json" params:params success:^(id responseObj) { 68 //        //字典转模型 69 //        YYUserModel *user=[YYUserModel objectWithKeyValues:responseObj]; 70 //        //设置标题 71 //        [self.titleButton setTitle:user.name forState:UIControlStateNormal]; 72 //       // 存储账号信息(需要先拿到账号) 73 //        YYAccountModel *account=[YYAccountTool accountModel]; 74 //        account.name=user.name; 75 //        //存储 76 //        [YYAccountTool save:account]; 77 //    } failure:^(NSError *error) { 78 //        YYLog(@"请求失败"); 79 //    }]; 80 } 81  82 //集成刷新控件 83 -(void)setupRefresh 84 { 85     // 1.添加下拉刷新控件 86     UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; 87     [self.tableView addSubview:refreshControl]; 88      89     //2.监听状态 90     [refreshControl addTarget:self action:(@selector(refreshControlStateChange:)) forControlEvents:UIControlEventValueChanged]; 91      92     //3.让刷新控件自动进入到刷新状态 93     [refreshControl beginRefreshing]; 94      95     //4.手动调用方法,加载数据 96     //模拟网络延迟,延迟2.0秒 97     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 98         [self refreshControlStateChange:refreshControl]; 99     });100     101     //5.上拉刷新数据102     YYloadStatusesFooter *footer=[YYloadStatusesFooter loadFooter];103     self.tableView.tableFooterView=footer;104     self.footer=footer;105 }106 107 /**108  *  当下拉刷新控件进入刷新状态(转圈圈)的时候会自动调用109  */110 -(void)refreshControlStateChange:(UIRefreshControl *)refreshControl111 {112     [self loadNewStatuses:refreshControl];113     114 //    //1.封装请求参数115 //    NSMutableDictionary *params=[NSMutableDictionary dictionary];116 //    params[@"access_token"] =[YYAccountTool accountModel].access_token;117 //    //取出当前微博模型中的第一条数据,获取第一条数据的id118 //    YYStatusModel *firstStatus=[self.statuses firstObject];119 //    if (firstStatus) {120 //        params[@"since_id"]=firstStatus.idstr;121 //    }122 //    123 //    //2.发送网络请求124                       //https://api.weibo.com/2/statuses/home_timeline.json125 //    [YYHttpTool get:@"https://api.weibo.com/2/statuses/home_timeline.json" params:params success:^(id responseObj) {126 //        127 //        // 微博字典 -- 数组128 //        NSArray *statusDictArray = responseObj[@"statuses"];129 //        //微博字典数组---》微博模型数组130 //        NSArray *newStatuses =[YYStatusModel objectArrayWithKeyValuesArray:statusDictArray];131 //        132 //        //把新数据添加到旧数据的前面133 //        NSRange range=NSMakeRange(0, newStatuses.count);134 //        NSIndexSet *indexSet=[NSIndexSet indexSetWithIndexesInRange:range];135 //        [self.statuses insertObjects:newStatuses atIndexes:indexSet];136 //        YYLog(@"刷新了--%d条新数据",newStatuses.count);137 //        138 //        //重新刷新表格139 //        [self.tableView reloadData];140 //        //让刷新控件停止刷新(回复默认的状态)141 //        [refreshControl endRefreshing];142 //        [self showNewStatusesCount:newStatuses.count];143 //        144 //    } failure:^(NSError *error) {145 //        146 //        YYLog(@"请求失败");147 //        //让刷新控件停止刷新(回复默认的状态)148 //        [refreshControl endRefreshing];149 //    }];150 }151 152 #pragma mark-加载微博数据153 -(void)loadNewStatuses:(UIRefreshControl *)refreshControl154 {155     //1.封装请求参数156     YYHomeStatusesParam *param=[[YYHomeStatusesParam alloc]init];157     param.access_token=[YYAccountTool accountModel].access_token;158     YYStatusModel *firstStatus=[self.statuses firstObject];159     if (firstStatus) {160         param.since_id=@([firstStatus.idstr longLongValue]);161     }162     163     //2.加载微博数据164     [YYStatusTool homeStatusesWithParam:param success:^(YYHomeStatusesResult *result) {165 //        // 微博字典 -- 数组166 //        NSArray *statusDictArray = responseObj[@"statuses"];167 //        //微博字典数组---》微博模型数组168 //        NSArray *newStatuses =[YYStatusModel objectArrayWithKeyValuesArray:statusDictArray];169         170         //获取最新的微博数组171         NSArray *newStatuses=result.statuses;172         173         //把新数据添加到旧数据的前面174         NSRange range=NSMakeRange(0, newStatuses.count);175         NSIndexSet *indexSet=[NSIndexSet indexSetWithIndexesInRange:range];176         [self.statuses insertObjects:newStatuses atIndexes:indexSet];177         YYLog(@"刷新了--%d条新数据",newStatuses.count);178         179         //重新刷新表格180         [self.tableView reloadData];181         //让刷新控件停止刷新(回复默认的状态)182         [refreshControl endRefreshing];183         [self showNewStatusesCount:newStatuses.count];184 185     } failure:^(NSError *error) {186         YYLog(@"请求失败");187         //让刷新控件停止刷新(回复默认的状态)188         [refreshControl endRefreshing];189 190     }];191     192 }193 194 /**195  *  加载更多的微博数据196  */197 - (void)loadMoreStatuses198 {199     // 1.封装请求参数200 //    NSMutableDictionary *params = [NSMutableDictionary dictionary];201 //    params[@"access_token"] = [YYAccountTool accountModel].access_token;202 //    YYStatusModel *lastStatus =  [self.statuses lastObject];203 //    if (lastStatus) {204 //        params[@"max_id"] = @([lastStatus.idstr longLongValue] - 1);205 //    }206     207 //    //2.发送网络请求208 //    [YYHttpTool get:@"https://api.weibo.com/2/statuses/home_timeline.json" params:params success:^(id responseObj) {209 //        // 微博字典数组210 //        NSArray *statusDictArray = responseObj[@"statuses"];211 //        // 微博字典数组 ---> 微博模型数组212 //        NSArray *newStatuses = [YYStatusModel objectArrayWithKeyValuesArray:statusDictArray];213 //    214 //        // 将新数据插入到旧数据的最后面215 //        [self.statuses addObjectsFromArray:newStatuses];216 //        217 //        // 重新刷新表格218 //        [self.tableView reloadData];219 //        220 //        // 让刷新控件停止刷新(恢复默认的状态)221 //        [self.footer endRefreshing];222 //223 //    } failure:^(NSError *error) {224 //        YYLog(@"请求失败--%@", error);225 //        // 让刷新控件停止刷新(恢复默认的状态)226 //        [self.footer endRefreshing];227 //    }];228     229     //1.封装请求参数230     YYHomeStatusesParam *param=[[YYHomeStatusesParam alloc]init];231     param.access_token=[YYAccountTool accountModel].access_token;232     YYStatusModel *lastStatus=[self.statuses lastObject];233     if (lastStatus) {234         param.max_id=@([lastStatus.idstr longLongValue]-1);235     }236     237     //2.发送网络请求238     [YYStatusTool homeStatusesWithParam:param success:^(YYHomeStatusesResult *result) {239 //        // 微博字典数组240 //        NSArray *statusDictArray = responseObj[@"statuses"];241 //        // 微博字典数组 ---> 微博模型数组242 //        NSArray *newStatuses = [YYStatusModel objectArrayWithKeyValuesArray:statusDictArray];243         244         //获取最新的微博数组245         NSArray *newStatuses=result.statuses;246         // 将新数据插入到旧数据的最后面247         [self.statuses addObjectsFromArray:newStatuses];248         // 重新刷新表格249         [self.tableView reloadData];250         // 让刷新控件停止刷新(恢复默认的状态)251         [self.footer endRefreshing];252     } failure:^(NSError *error) {253         YYLog(@"请求失败--%@", error);254         // 让刷新控件停止刷新(恢复默认的状态)255         [self.footer endRefreshing];256     }];257 }258 259 /**260  *  提示用户最新的微博数量261  *262  *  @param count 最新的微博数量263  */264 -(void)showNewStatusesCount:(int)count265 {266     //1.创建一个label267     UILabel *label=[[UILabel alloc]init];268     269     //2.设置label的文字270     if (count) {271         label.text=[NSString stringWithFormat:@"共有%d条新的微博数据",count];272     }else273     {274         label.text=@"没有最新的微博数据";275     }276     277     //3.设置label的背景和对其等属性278     label.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageWithName:@"timeline_new_status_background"]];279     label.textAlignment=UITextAlignmentCenter;280     label.textColor=[UIColor whiteColor];281     282     //4.设置label的frame283     label.x=0;284     label.width=self.view.width;285     label.height=35;286 //    label.y=64-label.height;287    label.y=self.navigationController.navigationBar.height+20-label.height;288     289     //5.把lable添加到导航控制器的View上290 //    [self.navigationController.view addSubview:label];291     //把label添加到导航控制器上,显示在导航栏的下面292     [self.navigationController.view insertSubview:label belowSubview:self.navigationController.navigationBar];293     294     //6.设置动画效果295     CGFloat duration=0.75;296     //设置提示条的透明度297     label.alpha=0.0;298    [UIView animateWithDuration:duration animations:^{299        //往下移动一个label的高度300        label.transform=CGAffineTransformMakeTranslation(0, label.height);301        label.alpha=1.0;302    } completion:^(BOOL finished) {//向下移动完毕303        304        //延迟delay秒的时间后,在执行动画305        CGFloat delay=0.5;306        307        [UIView animateKeyframesWithDuration:duration delay:delay options:UIViewAnimationOptionCurveEaseOut animations:^{308            309            //恢复到原来的位置310            label.transform=CGAffineTransformIdentity;311            label.alpha=0.0;312            313        } completion:^(BOOL finished) {314            315            //删除控件316            [label removeFromSuperview];317        }];318    }];319 }320 321 /**322  UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // 开始:由慢到快,结束:由快到慢323  UIViewAnimationOptionCurveEaseIn               = 1 << 16, // 由慢到块324  UIViewAnimationOptionCurveEaseOut              = 2 << 16, // 由快到慢325  UIViewAnimationOptionCurveLinear               = 3 << 16, // 线性,匀速326  */327 328 /**设置导航栏内容*/329 -(void)setupNavBar330 {331     self.navigationItem.leftBarButtonItem=[UIBarButtonItem itemWithImageName:@"navigationbar_friendsearch" highImageName:@"navigationbar_friendsearch_highlighted" target:self action:@selector(friendsearch)];332     self.navigationItem.rightBarButtonItem=[UIBarButtonItem itemWithImageName:@"navigationbar_pop" highImageName:@"navigationbar_pop_highlighted" target:self action:@selector(pop)];333     334     //设置导航栏按钮335     YYTitleButton *titleButton=[[YYTitleButton alloc]init];336     337     //设置尺寸338 //    titleButton.width=100;339         titleButton.height=35;340 341     //设置文字342     YYAccountModel *account= [YYAccountTool accountModel];343     NSString *name=account.name;344     NSLog(@"%@",name);345 //    name=@"yangye";346     //判断:如果name有值(上一次登录的用户名),那么就使用上次的,如果没有那么就设置为首页347     if (name) {348         [titleButton setTitle:name forState:UIControlStateNormal];349     }else{350         [titleButton setTitle:@"首页" forState:UIControlStateNormal];351     }352     //设置图标353     [titleButton setImage:[UIImage imageWithName:@"navigationbar_arrow_down"] forState:UIControlStateNormal];354     //设置背景355     [titleButton setBackgroundImage:[UIImage resizedImage:@"navigationbar_filter_background_highlighted"] forState:UIControlStateHighlighted];356     357     //设置尺寸358 //    titleButton.width=100;359 //    titleButton.height=35;360     //监听按钮的点击事件361     [titleButton addTarget:self action:@selector(titleButtonClick:) forControlEvents:UIControlEventTouchUpInside];362     self.navigationItem.titleView=titleButton;363     self.titleButton=titleButton;364 }365 -(void)titleButtonClick:(UIButton *)titleButton366 {367 368         [titleButton setImage:[UIImage imageWithName:@"navigationbar_arrow_up"] forState:UIControlStateNormal];369         370         UITableView *tableView=[[UITableView alloc]init];371         [tableView setBackgroundColor:[UIColor yellowColor]];372         YYPopMenu *menu=[YYPopMenu popMenuWithContentView:tableView];373         [menu showInRect:CGRectMake(60, 55, 200, 200)];374         menu.dimBackground=YES;375 376     menu.arrowPosition=YYPopMenuArrowPositionRight;377         menu.delegate=self;378 }379 380 381 #pragma mark-YYPopMenuDelegate382 //弹出菜单383 -(void)popMenuDidDismissed:(YYPopMenu *)popMenu384 {385     YYTitleButton *titleButton=(YYTitleButton *)self.navigationItem.titleView;386     [titleButton setImage:[UIImage imageWithName:@"navigationbar_arrow_down"] forState:UIControlStateNormal];387 }388 -(void)pop389 {390     YYLog(@"---POP---");391 }392 -(void)friendsearch393 {394     //跳转到one这个子控制器界面395     YYOneViewController *one=[[YYOneViewController alloc]init];396     one.title=@"One";397     //拿到当前控制器398     [self.navigationController pushViewController:one animated:YES];399     400 }401 402 #pragma mark - Table view data source403 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section404 {405 #warning 监听tableView每次显示数据的过程406     //在tableView显示之前,判断有没有数据,如有有数据那么就显示底部视图407     self.footer.hidden=self.statuses.count==0;408     return self.statuses.count;409 }410 411 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath412 {413     static NSString *ID = @"cell";414     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];415     if (!cell) {416         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];417     }418     419       //取出这行对应的微博字典数据,转换为数据模型420     YYStatusModel *status=self.statuses[indexPath.row];421     cell.textLabel.text=status.text;422     cell.detailTextLabel.text=status.user.name;423     NSString *imageUrlStr=status.user.profile_image_url;424     [cell.imageView setImageWithURL:[NSURL URLWithString:imageUrlStr] placeholderImage:[UIImage imageWithName:@"avatar_default_small"]];425     426     return cell;427 }428 429 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath430 {431     //点击cell的时候,跳到下一个界面432     UIViewController *newVc = [[UIViewController alloc] init];433     newVc.view.backgroundColor = [UIColor redColor];434     newVc.title = @"新控制器";435     [self.navigationController pushViewController:newVc animated:YES];436 }437 438 #pragma mark-代理方法439 - (void)scrollViewDidScroll:(UIScrollView *)scrollView440 {441     if (self.statuses.count <= 0 || self.footer.isRefreshing) return;442     443     // 1.差距444     CGFloat delta = scrollView.contentSize.height - scrollView.contentOffset.y;445     // 刚好能完整看到footer的高度446     CGFloat sawFooterH = self.view.height - self.tabBarController.tabBar.height;447     448     // 2.如果能看见整个footer449     if (delta <= (sawFooterH - 0)) {450         // 进入上拉刷新状态451         [self.footer beginRefreshing];452         453         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{454             // 加载更多的微博数据455             [self loadMoreStatuses];456         });457     }458 }459 @end

5.项目分层架构示意图

部分项目文件结构截图

项目分层:

步骤:

(1)新建一个模型类封装请求参数

(2)新建一个模型类封装请求结果(返回结果)

(3)新建一个业务类封装专一的业务