IOS第四天-新浪微博 -存储优化OAuth授权账号信息,下拉刷新,字典转模型
*************application
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 1.创建窗口
self.window = [[UIWindow alloc] init];
self.window.frame = [UIScreen mainScreen].bounds; // 2.设置根控制器
HWAccount *account = [HWAccountTool account];
if (account) { // 之前已经登录成功过
[self.window switchRootViewController];
} else {
self.window.rootViewController = [[HWOAuthViewController alloc] init]; //授权登陆的界面
} // 3.显示窗口
[self.window makeKeyAndVisible];
return YES;
}
*************存储账号的信息HWAccountTool.m
// 账号的存储路径
#define HWAccountPath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.archive"] #import "HWAccountTool.h"
#import "HWAccount.h" @implementation HWAccountTool /**
* 存储账号信息
*
* @param account 账号模型
*/
+ (void)saveAccount:(HWAccount *)account
{
// 获得账号存储的时间(accessToken的产生时间)
account.created_time = [NSDate date]; // 自定义对象的存储必须用NSKeyedArchiver,不再有什么writeToFile方法
[NSKeyedArchiver archiveRootObject:account toFile:HWAccountPath];
} /**
* 返回账号信息
*
* @return 账号模型(如果账号过期,返回nil)
*/
+ (HWAccount *)account
{
// 加载模型
HWAccount *account = [NSKeyedUnarchiver unarchiveObjectWithFile:HWAccountPath]; /* 验证账号是否过期 */ // 过期的秒数
long long expires_in = [account.expires_in longLongValue];
// 获得过期时间
NSDate *expiresTime = [account.created_time dateByAddingTimeInterval:expires_in];
// 获得当前时间
NSDate *now = [NSDate date]; // 如果expiresTime <= now,过期
/**
NSOrderedAscending = -1L, 升序,右边 > 左边
NSOrderedSame, 一样
NSOrderedDescending 降序,右边 < 左边
*/
NSComparisonResult result = [expiresTime compare:now];
if (result != NSOrderedDescending) { // 过期
return nil;
} return account;
}
@end
*******HWAccount.m
#import "HWAccount.h" @implementation HWAccount
+ (instancetype)accountWithDict:(NSDictionary *)dict
{
HWAccount *account = [[self alloc] init];
account.access_token = dict[@"access_token"];
account.uid = dict[@"uid"];
account.expires_in = dict[@"expires_in"];
return account;
} /**
* 当一个对象要归档进沙盒中时,就会调用这个方法
* 目的:在这个方法中说明这个对象的哪些属性要存进沙盒
*/
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.access_token forKey:@"access_token"];
[encoder encodeObject:self.expires_in forKey:@"expires_in"];
[encoder encodeObject:self.uid forKey:@"uid"];
[encoder encodeObject:self.created_time forKey:@"created_time"];
} /**
* 当从沙盒中解档一个对象时(从沙盒中加载一个对象时),就会调用这个方法
* 目的:在这个方法中说明沙盒中的属性该怎么解析(需要取出哪些属性)
*/
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init]) {
self.access_token = [decoder decodeObjectForKey:@"access_token"];
self.expires_in = [decoder decodeObjectForKey:@"expires_in"];
self.uid = [decoder decodeObjectForKey:@"uid"];
self.created_time = [decoder decodeObjectForKey:@"created_time"];
}
return self;
}
@end
*********HWAccount.h
#import <Foundation/Foundation.h> @interface HWAccount : NSObject <NSCoding>
/** string 用于调用access_token,接口获取授权后的access token。*/
@property (nonatomic, copy) NSString *access_token; /** string access_token的生命周期,单位是秒数。*/
@property (nonatomic, copy) NSNumber *expires_in; /** string 当前授权用户的UID。*/
@property (nonatomic, copy) NSString *uid; /** access token的创建时间 */
@property (nonatomic, strong) NSDate *created_time; + (instancetype)accountWithDict:(NSDictionary *)dict;
@end
**********HWOAuthViewController.m
#import "HWOAuthViewController.h"
#import "AFNetworking.h"
#import "HWAccount.h"
#import "HWAccountTool.h"
#import "MBProgressHUD+MJ.h" @interface HWOAuthViewController () <UIWebViewDelegate> @end @implementation HWOAuthViewController - (void)viewDidLoad
{
[super viewDidLoad]; // 1.创建一个webView
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = self.view.bounds;
webView.delegate = self;
[self.view addSubview:webView]; // 2.用webView加载登录页面(新浪提供的)
// 请求地址:https://api.weibo.com/oauth2/authorize
/* 请求参数:
client_id true string 申请应用时分配的AppKey。
redirect_uri true string 授权回调地址,站外应用需与设置的回调地址一致,站内应用需填写canvas page的地址。
*/
NSURL *url = [NSURL URLWithString:@"https://api.weibo.com/oauth2/authorize?client_id=3235932662&redirect_uri=http://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
} #pragma mark - webView代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[MBProgressHUD hideHUD];
} - (void)webViewDidStartLoad:(UIWebView *)webView
{
[MBProgressHUD showMessage:@"正在加载..."];
} - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[MBProgressHUD hideHUD];
} - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// 1.获得url
NSString *url = request.URL.absoluteString; // 2.判断是否为回调地址
NSRange range = [url rangeOfString:@"code="];
if (range.length != ) { // 是回调地址
// 截取code=后面的参数值
int fromIndex = range.location + range.length;
NSString *code = [url substringFromIndex:fromIndex]; // 利用code换取一个accessToken
[self accessTokenWithCode:code]; // 禁止加载回调地址
return NO;
} return YES;
} /**
* 利用code(授权成功后的request token)换取一个accessToken
*
* @param code 授权成功后的request token
*/
- (void)accessTokenWithCode:(NSString *)code
{
/*
URL:https://api.weibo.com/oauth2/access_token 请求参数:
client_id:申请应用时分配的AppKey
client_secret:申请应用时分配的AppSecret
grant_type:使用authorization_code
redirect_uri:授权成功后的回调地址
code:授权成功后返回的code
*/
// 1.请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// mgr.responseSerializer = [AFJSONResponseSerializer serializer];
// AFN的AFJSONResponseSerializer默认不接受text/plain这种类型 // 2.拼接请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"client_id"] = @"";
params[@"client_secret"] = @"227141af66d895d0dd8baca62f73b700";
params[@"grant_type"] = @"authorization_code";
params[@"redirect_uri"] = @"http://www.baidu.com";
params[@"code"] = code; // 3.发送请求
[mgr POST:@"https://api.weibo.com/oauth2/access_token" parameters:params success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) {
[MBProgressHUD hideHUD]; // 将返回的账号字典数据 --> 模型,存进沙盒
HWAccount *account = [HWAccount accountWithDict:responseObject];
// 存储账号信息
[HWAccountTool saveAccount:account]; // 切换窗口的根控制器
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window switchRootViewController]; // UIWindow的分类、HWWindowTool
// UIViewController的分类、HWControllerTool
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[MBProgressHUD hideHUD];
HWLog(@"请求失败-%@", error);
}];
}
@end
UIWindow+Extension.m
#import "UIWindow+Extension.h"
#import "HWTabBarViewController.h"
#import "HWNewfeatureViewController.h" @implementation UIWindow (Extension)
- (void)switchRootViewController
{
NSString *key = @"CFBundleVersion";
// 上一次的使用版本(存储在沙盒中的版本号)
NSString *lastVersion = [[NSUserDefaults standardUserDefaults] objectForKey:key];
// 当前软件的版本号(从Info.plist中获得)
NSString *currentVersion = [NSBundle mainBundle].infoDictionary[key]; if ([currentVersion isEqualToString:lastVersion]) { // 版本号相同:这次打开和上次打开的是同一个版本
self.rootViewController = [[HWTabBarViewController alloc] init];
} else { // 这次打开的版本和上一次不一样,显示新特性
self.rootViewController = [[HWNewfeatureViewController alloc] init]; // 将当前的版本号存进沙盒
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
@end
************HWHomeViewController.m
#import "HWHomeViewController.h"
#import "HWDropdownMenu.h"
#import "HWTitleMenuViewController.h"
#import "AFNetworking.h"
#import "HWAccountTool.h"
#import "HWTitleButton.h"
#import "UIImageView+WebCache.h"
#import "HWUser.h"
#import "HWStatus.h"
#import "MJExtension.h" // 第三方的框架 @interface HWHomeViewController () <HWDropdownMenuDelegate>
/**
* 微博数组(里面放的都是HWStatus模型,一个HWStatus对象就代表一条微博)
*/
@property (nonatomic, strong) NSMutableArray *statuses;
@end @implementation HWHomeViewController - (NSMutableArray *)statuses
{
if (!_statuses) {
self.statuses = [NSMutableArray array];
}
return _statuses;
} - (void)viewDidLoad
{
[super viewDidLoad]; // 设置导航栏内容
[self setupNav]; // 获得用户信息(昵称)
[self setupUserInfo]; // 集成刷新控件
[self setupRefresh];
} /**
* 3. 集成刷新控件
*/
- (void)setupRefresh
{
UIRefreshControl *control = [[UIRefreshControl alloc] init];
[control addTarget:self action:@selector(refreshStateChange:) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:control];
} /**
* 3-1 UIRefreshControl进入刷新状态:加载最新的数据
*/
- (void)refreshStateChange:(UIRefreshControl *)control
{
// 1.请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager]; // 2.拼接请求参数
HWAccount *account = [HWAccountTool account];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"access_token"] = account.access_token; // 取出最前面的微博(最新的微博,ID最大的微博)
HWStatus *firstStatus = [self.statuses firstObject];
if (firstStatus) {
// 若指定此参数,则返回ID比since_id大的微博(即比since_id时间晚的微博),默认为0
params[@"since_id"] = firstStatus.idstr;
} // 3.发送请求
[mgr GET:@"https://api.weibo.com/2/statuses/friends_timeline.json" parameters:params success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) {
// 将 "微博字典"数组 转为 "微博模型"数组
NSArray *newStatuses = [HWStatus objectArrayWithKeyValuesArray:responseObject[@"statuses"]]; //第三方的框架 // 将最新的微博数据,添加到总数组的最前面
NSRange range = NSMakeRange(, newStatuses.count);
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
[self.statuses insertObjects:newStatuses atIndexes:set]; // 刷新表格
[self.tableView reloadData]; // 结束刷新刷新
[control endRefreshing];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
HWLog(@"请求失败-%@", error); // 结束刷新刷新
[control endRefreshing];
}];
} /**
* 2. 获得用户信息(昵称)
*/
- (void)setupUserInfo
{
// 1.请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager]; // 2.拼接请求参数
HWAccount *account = [HWAccountTool account];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"access_token"] = account.access_token;
params[@"uid"] = account.uid; // 3.发送请求
[mgr GET:@"https://api.weibo.com/2/users/show.json" parameters:params success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) {
// 标题按钮
UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
// 设置名字
HWUser *user = [HWUser objectWithKeyValues:responseObject];
[titleButton setTitle:user.name forState:UIControlStateNormal]; // 存储昵称到沙盒中
account.name = user.name;
[HWAccountTool saveAccount:account];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
HWLog(@"请求失败-%@", error);
}];
} /**
* 1. 设置导航栏内容
*/
- (void)setupNav
{
/* 设置导航栏上面的内容 */
self.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithTarget:self action:@selector(friendSearch) image:@"navigationbar_friendsearch" highImage:@"navigationbar_friendsearch_highlighted"];
self.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithTarget:self action:@selector(pop) image:@"navigationbar_pop" highImage:@"navigationbar_pop_highlighted"]; /* 中间的标题按钮 */
HWTitleButton *titleButton = [[HWTitleButton alloc] init];
// 设置图片和文字
NSString *name = [HWAccountTool account].name;
[titleButton setTitle:name?name:@"首页" forState:UIControlStateNormal];
// 监听标题点击
[titleButton addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.titleView = titleButton;
} /**
* 1-1 标题点击
*/
- (void)titleClick:(UIButton *)titleButton
{
// 1.创建下拉菜单
HWDropdownMenu *menu = [HWDropdownMenu menu];
menu.delegate = self; // 2.设置内容
HWTitleMenuViewController *vc = [[HWTitleMenuViewController alloc] init];
vc.view.height = ;
vc.view.width = ;
menu.contentController = vc; // 3.显示
[menu showFrom:titleButton];
} - (void)friendSearch
{
NSLog(@"friendSearch");
} - (void)pop
{
NSLog(@"pop");
} #pragma mark - HWDropdownMenuDelegate
/**
* 下拉菜单被销毁了
*/
- (void)dropdownMenuDidDismiss:(HWDropdownMenu *)menu
{
UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
// 让箭头向下
titleButton.selected = NO;
} /**
* 下拉菜单显示了
*/
- (void)dropdownMenuDidShow:(HWDropdownMenu *)menu
{
UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
// 让箭头向上
titleButton.selected = YES;
} #pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.statuses.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"status";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
} // 取出这行对应的微博字典
HWStatus *status = self.statuses[indexPath.row]; // 取出这条微博的作者(用户)
HWUser *user = status.user;
cell.textLabel.text = user.name; // 设置微博的文字
cell.detailTextLabel.text = status.text; // 设置头像
UIImage *placehoder = [UIImage imageNamed:@"avatar_default_small"];
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:user.profile_image_url] placeholderImage:placehoder]; return cell;
} /**
1.将字典转为模型
2.能够下拉刷新最新的微博数据
3.能够上拉加载更多的微博数据
*/
@end
***HWTitleButton.h
#import "HWTitleButton.h" @implementation HWTitleButton - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.titleLabel.font = [UIFont boldSystemFontOfSize:];
[self setImage:[UIImage imageNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal];
[self setImage:[UIImage imageNamed:@"navigationbar_arrow_up"] forState:UIControlStateSelected];
}
return self;
} - (void)layoutSubviews
{
[super layoutSubviews];
// 如果仅仅是调整按钮内部titleLabel和imageView的位置,那么在layoutSubviews中单独设置位置即可 // 1.计算titleLabel的frame
self.titleLabel.x = self.imageView.x; // 2.计算imageView的frame
self.imageView.x = CGRectGetMaxX(self.titleLabel.frame);
} - (void)setTitle:(NSString *)title forState:(UIControlState)state
{
[super setTitle:title forState:state]; // 只要修改了文字,就让按钮重新计算自己的尺寸
[self sizeToFit];
} - (void)setImage:(UIImage *)image forState:(UIControlState)state
{
[super setImage:image forState:state]; // 只要修改了图片,就让按钮重新计算自己的尺寸
[self sizeToFit];
}
@end
#import <UIKit/UIKit.h> @interface HWTitleButton : UIButton @end
HWStatus.h
#import <Foundation/Foundation.h>
@class HWUser; @interface HWStatus : NSObject
/** string 字符串型的微博ID*/
@property (nonatomic, copy) NSString *idstr; /** string 微博信息内容*/
@property (nonatomic, copy) NSString *text; /** object 微博作者的用户信息字段 详细*/
@property (nonatomic, strong) HWUser *user;
@end
HWStatus.m
#import "HWStatus.h" @implementation HWStatus
@end
IOS第四天-新浪微博 -存储优化OAuth授权账号信息,下拉刷新,字典转模型的更多相关文章
- iOS开源项目推荐|下拉刷新
MJRefresh - 仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能.可以自定义上下拉刷新的文字说明. CBStoreHouseRefresh ...
- IOS学习笔记34—EGOTableViewPullRefresh实现下拉刷新
移动应用开发中有这么一种场景,就是在列表中显示的数据刷新,有点击刷新按钮刷新的,也有现在最流行的由Twitter首先推出的下拉刷新功能,在IOS中,使用下拉刷新更新UITableView中的数据也用的 ...
- iOS下拉刷新和上拉刷新
在iOS开发中,我们经常要用到下拉刷新和上拉刷新来加载新的数据,当前这也适合分页.iOS原生就带有该方法,下面就iOS自带的下拉刷新方法来简单操作. 上拉刷新 1.在TableView里,一打开软件, ...
- 高仿IOS下拉刷新的粘虫效果
最近看需要做一款下拉刷新的效果,由于需要和Ios界面保持一致,所以这用安卓的方式实现了ios下的下拉刷新的粘虫效果. 最新的安卓手机版本的QQ也有这种类似的效果,就是拖动未读信息的那个红色圆圈,拖动近 ...
- IOS tableview下拉刷新上拉加载分页
http://code4app.com/ios/快速集成下拉上拉刷新/52326ce26803fabc46000000 刷新没用用插件,加载使用的MJ老师的插件. - (void)viewDidLoa ...
- IOS 开发下拉刷新和上拉加载更多
IOS 开发下拉刷新和上拉加载更多 简介 1.常用的下拉刷新的实现方式 (1)UIRefreshControl (2)EGOTTableViewrefresh (3)AH3DPullRefresh ( ...
- IOS UITableView下拉刷新和上拉加载功能的实现
在IOS开发中UITableView是非常常用的一个功能,而在使用UITableView的时候我们经常要用到下拉刷新和上拉加载的功能,今天花时间实现了简单的UITableView的下拉刷新和上拉加载功 ...
- 简单的下拉刷新以及优化--SwipeRefreshLayout
代码工程简要说明:以一个SwipeRefreshLayout包裹ListView,SwipeRefreshLayout接管ListView的下拉事件,若ListView被用户触发下拉动作后,Swipe ...
- iOS开发 XML解析和下拉刷新,上拉加载更多
iOS开发 XML解析和下拉刷新,上拉加载更多 1.XML格式 <?xml version="1.0" encoding="utf-8" ?> 表示 ...
随机推荐
- AOP 面向切面编程, Attribute在项目中的应用
一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...
- Java基础学习(三)
/* java中的八种基本数据类型: 整数: byte . short . int . long 小数: float double 字符: char 布尔: boolean 字符串的类型: Strin ...
- 【DWR系列06】- DWR日志及js压缩
img { border: solid 1px } 一.日志 DWR依赖 Apache Commons Logging,可以使用log4j实现日志记录功能. 1.1 日志简介 和其他日志框架一样,当设 ...
- SQL Server 系统数据库
Sql Server的系统数据库分为:master.model.msdb,resouce和tempdb,这五个数据库在SQL Server中各司其职,作为研发人员,很有必要了解这几个数据库的职责,下面 ...
- win10常用帮助
添加自启动项: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp shell:startup win10找回图片查看器: Win ...
- android_studio上传svn的时候那些不提交
buid文件夹不需要提交
- 创建Unity3D的MacOS Plugin的正确姿势
http://www.tedlindstrom.se/how-to-link-dylibs-into-unity3d/ I was roaming around the net looking for ...
- 深入理解ConcurrentMap.putIfAbsent(key,value) 用法
转自:http://blog.csdn.net/exceptional_derek/article/details/40384659 先看一段代码: public class Locale { pri ...
- 从外部浏览开启app
先描述一下需求:从浏览器中点击某个按钮,如果手机上装有相应的app,则直接开启app,并且到相对的页面.如果没有装该app,则会到相应的下载app的界面. 我这里主要用的是第三方的东西,就是魔窗中的m ...
- Delphi 中的自动释放策略-转
八.使用结构体而不是结构体指针: 很重要 一.指定 Owner 后, 随 Owner 连带释放: //uses Vcl.StdCtrls, Vcl.ExtCtrls; var panel: TPane ...