IOS第四天-新浪微博 -存儲優化OAuth受權帳號信息,下拉刷新,字典轉模型

*************applicationweb

- (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.mjson

// 帳號的存儲路徑
#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.mcanvas

#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.hapi

#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 != 0) { // 是回調地址
        // 截取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"] = @"3235932662";
    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.mapp

#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(0, 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 = 150;
    vc.view.width = 150;
    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.hide

#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:17];
        [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.hui

#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.matom

#import "HWStatus.h"

@implementation HWStatus
@end
相關文章
相關標籤/搜索