标签:style blog http color 使用 strong
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 : NSObject 10 /** 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 *))failure 11 { 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 YYHomeStatusesResult 10 -(NSDictionary *)objectClassInArray 11 { 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 *)refreshControl 111 { 112 [self loadNewStatuses:refreshControl]; 113 114 // //1.封装请求参数 115 // NSMutableDictionary *params=[NSMutableDictionary dictionary]; 116 // params[@"access_token"] =[YYAccountTool accountModel].access_token; 117 // //取出当前微博模型中的第一条数据,获取第一条数据的id 118 // 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.json 125 // [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 *)refreshControl 154 { 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)loadMoreStatuses 198 { 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)count 265 { 266 //1.创建一个label 267 UILabel *label=[[UILabel alloc]init]; 268 269 //2.设置label的文字 270 if (count) { 271 label.text=[NSString stringWithFormat:@"共有%d条新的微博数据",count]; 272 }else 273 { 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的frame 283 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)setupNavBar 330 { 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 *)titleButton 366 { 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-YYPopMenuDelegate 382 //弹出菜单 383 -(void)popMenuDidDismissed:(YYPopMenu *)popMenu 384 { 385 YYTitleButton *titleButton=(YYTitleButton *)self.navigationItem.titleView; 386 [titleButton setImage:[UIImage imageWithName:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 387 } 388 -(void)pop 389 { 390 YYLog(@"---POP---"); 391 } 392 -(void)friendsearch 393 { 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 source 403 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 404 { 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 *)indexPath 412 { 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 *)indexPath 430 { 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 *)scrollView 440 { 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.如果能看见整个footer 449 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)新建一个业务类封装专一的业务
iOS开发项目篇—36封装微博业务,布布扣,bubuko.com
标签:style blog http color 使用 strong
原文地址:http://www.cnblogs.com/wendingding/p/3849438.html