国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

寫給iOS小白的MVVM教程(一):從MVC到MVVM之一個典型的MVC應用場景

2019-11-14 18:08:19
字體:
來源:轉載
供稿:網友

前言

本著實踐為主的原則,此系列文章不做過多的概念性的闡述和討論;更多的代碼和篇幅用來展示MVC和MVVC下的基礎代碼結構與具體實現,來展示各自優劣.這篇文章,更多的在于發掘MVC與MVVC的共性,以期為那些對MVVC感興趣的iOS開發者,找到一種平滑的過渡與重構代碼的方式.如果對MVVC感興趣,可以直接將本文的大部分代碼引用到自己的項目中,畢竟代碼是寫出來的!開篇之前,你可以先到這里下載本文的示例工程: https://github.com/ios122/ios122

在這一篇章里,我會分別使用我所理解的MVC與MVVC兩種模式來完成同一個應用場景,以期幫助那些熟悉傳統MVC模式代碼的iOS攻城獅,能更好理解MVVC.限于篇幅,將MVC和MVVM拆分為兩個部分,今天要說的是一個典型的MVC的應用場景,為基于MVC的MVVM重構做個基礎.這篇文章著重進行了接口準備,必須的知識點的說明等內容.

預設場景:按分類請求一組博客,點擊獲取博客詳情

我們選取最常見的一組場景: 根據某種規則獲取一組數據,點擊某一條數據,可以跳轉到下一界面獲取數據詳情.這里我會根據分類請求此分類下的博客列表,點擊某一條信息,可跳轉到博客詳情頁.簡單說,其實我們真正需要實現的只有兩個頁面: 博客分類列表頁 與 博客詳情頁.

數據接口準備

我們至少需要兩個接口,一個可以根據分類來獲取博客列表,一個用來根據id獲取博客詳情.

使用預定義的接口

如果你沒有自己的服務器或者對服務器開發不熟悉,可以使用我準備的這兩個測試接口:

博客列表接口

http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=ui&model[page]=2
  • ui分類名稱,目前預定義支持: ui, network, tool,autolayout 四個分類.
  • 2,獲取第幾頁的數據,從0開始計數,指請求此分類下第幾頁的數據.預定義每個分類下有100條數據,每20條數據一頁.
  • 返回示例:
[    {        "id": "ui_40",        "title": "title_ui_40",        "desc": "desc_ui_40"    },    {        "id": "ui_41",        "title": "title_ui_41",        "desc": "desc_ui_41"    },    {        "id": "ui_42",        "title": "title_ui_42",        "desc": "desc_ui_42"    },    {        "id": "ui_43",        "title": "title_ui_43",        "desc": "desc_ui_43"    },    {        "id": "ui_44",        "title": "title_ui_44",        "desc": "desc_ui_44"    },    {        "id": "ui_45",        "title": "title_ui_45",        "desc": "desc_ui_45"    },    {        "id": "ui_46",        "title": "title_ui_46",        "desc": "desc_ui_46"    },    {        "id": "ui_47",        "title": "title_ui_47",        "desc": "desc_ui_47"    },    {        "id": "ui_48",        "title": "title_ui_48",        "desc": "desc_ui_48"    },    {        "id": "ui_49",        "title": "title_ui_49",        "desc": "desc_ui_49"    },    {        "id": "ui_50",        "title": "title_ui_50",        "desc": "desc_ui_50"    },    {        "id": "ui_51",        "title": "title_ui_51",        "desc": "desc_ui_51"    },    {        "id": "ui_52",        "title": "title_ui_52",        "desc": "desc_ui_52"    },    {        "id": "ui_53",        "title": "title_ui_53",        "desc": "desc_ui_53"    },    {        "id": "ui_54",        "title": "title_ui_54",        "desc": "desc_ui_54"    },    {        "id": "ui_55",        "title": "title_ui_55",        "desc": "desc_ui_55"    },    {        "id": "ui_56",        "title": "title_ui_56",        "desc": "desc_ui_56"    },    {        "id": "ui_57",        "title": "title_ui_57",        "desc": "desc_ui_57"    },    {        "id": "ui_58",        "title": "title_ui_58",        "desc": "desc_ui_58"    },    {        "id": "ui_59",        "title": "title_ui_59",        "desc": "desc_ui_59"    }]

2.博客詳情接口

http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=ui_0
  • ui_0 表示博客唯一標識.其應為分類博客列表返回的一個有效id.
  • 返回示例:
{    "title": "title of ui_0",    "body": "<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt=/"QQ/" src=/"https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png/" />"}

自定義接口

如果你有自己的服務器接口,直接使用即可;但是下面的oc代碼,你可能也要對應變換下;如果你對服務器接口開發不是很了解,可以先閱讀下這篇文章: iOS程序猿如何快速掌握 PHP,化身”全棧攻城獅”?.

假定,你已經閱讀并領會了 << iOS程序猿如何快速掌握 PHP,化身”全棧攻城獅”? >>,這篇文章,新建問及那,并把下面的代碼復制到對應文件中,然后根據自己的需要更改即可:

博客列表接口源文件

<?php // YFPostListViewController.phpclass YFPostListViewController{  public $model = array(); //!< 傳入的數據.  PRivate $countOfPerPage = 20; //!< 每頁數據條數.  /* 獲取內容,用于輸出顯示. */  protected function getContent()  {    /* 預定義一組數據 */    $datasource = array();    $categorys = array('ui', 'network', 'tool', 'autolayout');    for ($i=0; $i < count($categorys); $i++) {      $categoryName = $categorys[$i];      $categoryData = array();      for ($j=0; $j < 100; $j++) {        $item = array(          'id' => "{$categoryName}_{$j}",          'title' => "title_{$categoryName}_{$j}",          'desc' => "desc_{$categoryName}_{$j}"        );        $categoryData[$j] = $item;      }      $datasource[$categoryName] = $categoryData;    }    $queryCategoryName = $this->model['category'];    $queryPage = $this->model['page'];    $targetCategoryData = $datasource[$queryCategoryName];    $content = array();    for ($i = $this->countOfPerPage * $queryPage ; $i < $this->countOfPerPage * ($queryPage + 1); $i ++ ) {      $content[] = $targetCategoryData[$i];    }    $content = json_encode($content);     return $content;  }  public function show()  {   $content = $this->getContent();   header("Content-type: application/json");   echo $content;  }}

博客詳情接口源文件

<?php // YFPostViewController.phpclass YFPostViewController{  public $model = array(); //!< 傳入的數據.  /* 獲取內容,用于輸出顯示. */  protected function getContent()  {    $id = $this->model['id'];    $content = array(      'title' => "title of {$id}",      'body' => '<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt="qq" src="https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png" />'    );    $content = json_encode($content);     return $content;  }  public function show()  {   $content = $this->getContent();   header("Content-type: application/json");   echo $content;  }}

MVC 版本實現: 類似的代碼,你不知道敲過了多少遍

技術要點

下面列出將要用到的技術點,如有你不熟悉的,可點擊對應鏈接訪問:

  • 使用 AFNetworking 來處理網絡請求;
  • 使用 MJExtension實現JSON到數據模型的自動轉換;
  • 使用 MJRefresh 實現下拉刷新與上拉加載更多的效果;
  • 使用 Masonry 進行AutoLayout布局;
  • 使用 MBProgressHUD 優化頁面加載時的進度提示;

思路分析

  • 博客分類列表頁面:
  1. 在前一頁面指定博客分類;
  2. 頁面加載時自動發起網絡請求獲取對應分類的數據;
  3. 獲取數據成功后,自動刷新視圖;獲取失敗,則給出錯誤提示;
  4. 點擊某一條數據,可跳轉到博客詳情頁.
  • 博客詳情頁面:
  1. 在前一頁面指定博客id;
  2. 頁面加載時自動發起網絡請求獲取id的博客詳情;
  3. 獲取成功后,自動刷新視圖;獲取失敗,則給出錯誤提示.

博客列表頁面

博客列表效果圖

1. 在前一頁面指定博客分類;

這一步,大家肯定都會:

YFMVCPostListViewController * mvcPostListVC = [[YFMVCPostListViewController alloc] init];    mvcPostListVC.categoryName = @"ui";    [self.navigationController pushViewController: mvcPostListVC animated: YES];

2. 頁面加載時自動發起網絡請求獲取對應分類的數據;

為了保證每次都能進入列表頁,都能自動刷新數據,建議在 viewWillAppear:方法刷新數據:

- (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear: animated];        [self updateData];}

updateData方法進行數據的更新:

- (void)updateData{    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];        NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=%@&model[page]=0", self.categoryName];        [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responSEObject) {        NSLog(@"JSON: %@", responseObject);    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {        NSLog(@"Error: %@", error);    }];}

此處使用的是預定義接口,數據請求成功后,控制臺輸入如下:

JSON: (        {        desc = "desc_ui_0";        id = "ui_0";        title = "title_ui_0";    },        {        desc = "desc_ui_1";        id = "ui_1";        title = "title_ui_1";    },        {        desc = "desc_ui_2";        id = "ui_2";        title = "title_ui_2";    },        {        desc = "desc_ui_3";        id = "ui_3";        title = "title_ui_3";    },        {        desc = "desc_ui_4";        id = "ui_4";        title = "title_ui_4";    },        {        desc = "desc_ui_5";        id = "ui_5";        title = "title_ui_5";    },        {        desc = "desc_ui_6";        id = "ui_6";        title = "title_ui_6";    },        {        desc = "desc_ui_7";        id = "ui_7";        title = "title_ui_7";    },        {        desc = "desc_ui_8";        id = "ui_8";        title = "title_ui_8";    },        {        desc = "desc_ui_9";        id = "ui_9";        title = "title_ui_9";    },        {        desc = "desc_ui_10";        id = "ui_10";        title = "title_ui_10";    },        {        desc = "desc_ui_11";        id = "ui_11";        title = "title_ui_11";    },        {        desc = "desc_ui_12";        id = "ui_12";        title = "title_ui_12";    },        {        desc = "desc_ui_13";        id = "ui_13";        title = "title_ui_13";    },        {        desc = "desc_ui_14";        id = "ui_14";        title = "title_ui_14";    },        {        desc = "desc_ui_15";        id = "ui_15";        title = "title_ui_15";    },        {        desc = "desc_ui_16";        id = "ui_16";        title = "title_ui_16";    },        {        desc = "desc_ui_17";        id = "ui_17";        title = "title_ui_17";    },        {        desc = "desc_ui_18";        id = "ui_18";        title = "title_ui_18";    },        {        desc = "desc_ui_19";        id = "ui_19";        title = "title_ui_19";    })

3. 獲取數據成功后,自動刷新視圖;獲取失敗,則給出錯誤提示;

這一部分,涉及的變動較多,我就直接貼代碼了.你會注意到View和數據已經交叉進行了,很亂的感覺.而這也是我們想要使用MVVM重構代碼的重要原因之一.

////  YFMVCPostListViewController.m//  iOS122////  Created by 顏風 on 15/10/14.//  Copyright (c) 2015年 iOS122. All rights reserved.//#import "YFMVCPostListViewController.h"#import "YFArticleModel.h"#import <AFNetworking.h>#import <MJRefresh.h>#import <MBProgressHUD.h>@interface YFMVCPostListViewController ()<UITableViewDelegate, UITableViewDataSource>@property (nonatomic, strong) UITableView * tableView;@property (nonatomic, strong) NSMutableArray * articles; //!< 文章數組,內部存儲AFArticleModel類型.@property (assign, nonatomic) NSInteger page; //!< 數據頁數.表示下次請求第幾頁的數據.@end@implementation YFMVCPostListViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view.}- (NSMutableArray *)articles{    if (nil == _articles) {        _articles = [NSMutableArray arrayWithCapacity: 42];    }        return _articles;}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}- (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear: animated];    // 馬上進入刷新狀態    [self.tableView.header beginRefreshing];}- (UITableView *)tableView{    if (nil == _tableView) {        _tableView = [[UITableView alloc] init];                [self.view addSubview: _tableView];                [_tableView makeConstraints:^(MASConstraintMaker *make) {            make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));        }];                _tableView.delegate = self;        _tableView.dataSource = self;                NSString * cellReuseIdentifier = NSStringFromClass([UITableViewCell class]);                [_tableView registerClass: NSClassFromString(cellReuseIdentifier) forCellReuseIdentifier:cellReuseIdentifier];                _tableView.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{            self.page = 0;                        [self updateData];        }];                _tableView.footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{            [self updateData];        }];            }        return _tableView;}/** * 更新視圖. */- (void) updateView{    [self.tableView reloadData];}/** *  更新數據. * *  數據更新后,會自動更新視圖. */- (void)updateData{    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];        NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=%@&model[page]=%ld", self.categoryName, (long)self.page ++];        [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {        [self.tableView.header endRefreshing];        [self.tableView.footer endRefreshing];                if (1 == self.page) { // 說明是在重新請求數據.            self.articles = nil;        }                NSArray * responseArticles = [YFArticleModel objectArrayWithKeyValuesArray: responseObject];                [self.articles addObjectsFromArray: responseArticles];                [self updateView];    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {        [self.tableView.header endRefreshing];        [self.tableView.footer endRefreshing];                MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];        hud.mode = MBProgressHUDModeText;        hud.labelText = @"您的網絡不給力!";        [hud hide: YES afterDelay: 2];    }];}# pragma mark - tabelView代理方法.- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    NSInteger number  = self.articles.count;        return number;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    NSString * cellReuseIdentifier = NSStringFromClass([UITableViewCell class]);        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: cellReuseIdentifier forIndexPath:indexPath];        YFArticleModel * model = self.articles[indexPath.row];    NSString * content = [NSString stringWithFormat: @"標題:%@ 內容:%@", model.title, model.desc];        cell.textLabel.text = content;        return cell;}@end

4. 點擊某一條數據,可跳轉到博客詳情頁.

只需要再額外實現下 -tableView: didSelectRowAtIndexPath:方法即可:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    // 跳轉到博客詳情.    YFArticleModel * articleModel = self.articles[indexPath.row];        YFMVCPostViewController * postVC = [[YFMVCPostViewController alloc] init];        postVC.articleID = articleModel.id;        [self.navigationController pushViewController: postVC animated: YES];}

博客詳情頁面

博客詳情效果圖

1. 在前一頁面指定博客id;

這里其實就是博客列表的控制器的那幾句:

// 跳轉到博客詳情.YFArticleModel * articleModel = self.articles[indexPath.row];    YFMVCPostViewController * postVC = [[YFMVCPostViewController alloc] init];    postVC.articleID = articleModel.id;    [self.navigationController pushViewController: postVC animated: YES];

2. 頁面加載時自動發起網絡請求獲取id的博客詳情;

此處為了方便,我們依然使用預定義的博客詳情接口:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];        NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=%@", self.articleID];        [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {        NSLog(@"%@", responseObject);                [self updateView];    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {                MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];        hud.mode = MBProgressHUDModeText;        hud.labelText = @"您的網絡不給力!";        [hud hide: YES afterDelay: 2];    }];

請求的輸入,Xcode控制臺打印輸出,類似于:

{    body = "<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt=/"qq/" src=/"https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png/" />";    title = "title of ui_0";}

3. 獲取成功后,自動刷新視圖;獲取失敗,則給出錯誤提示.

你會注意到,我們在上一步獲取的數據,body部分內部是HTML字符串,所以我們要使用webView來顯示博客詳情.這和最近炒得很火的的混合開發模式有些像,但是目前主流的博客應用,幾乎都是這么做的.完整代碼如下:

////  YFMVCPostViewController.m//  iOS122////  Created by 顏風 on 15/10/16.//  Copyright (c) 2015年 iOS122. All rights reserved.//#import "YFMVCPostViewController.h"#import "YFArticleModel.h"#import <AFNetworking.h>#import <MBProgressHUD.h>@interface YFMVCPostViewController ()<UIWebViewDelegate>@property (strong, nonatomic) UIWebView * webView;@property (strong, nonatomic) YFArticleModel * article;@end@implementation YFMVCPostViewController- (UIWebView *)webView{    if (nil == _webView) {        _webView = [[UIWebView alloc] init];                [self.view addSubview: _webView];                [_webView makeConstraints:^(MASConstraintMaker *make) {            make.edges.equalTo(UIEdgeInsetsMake(64, 0, 0, 0));        }];    }        return _webView;}- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view.}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}- (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear: animated];        [self updateData];}/** * 更新視圖. */- (void) updateView{    [self.webView loaDHTMLString: self.article.body baseURL:nil];}/** *  更新數據. * *  數據更新后,會自動更新視圖. */- (void)updateData{    [MBProgressHUD showHUDAddedTo:self.view animated: YES];        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];        NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=%@", self.articleID];        [manager GET: urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {        self.article = [YFArticleModel objectWithKeyValues: responseObject];                [self updateView];    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {                MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];        hud.mode = MBProgressHUDModeText;        hud.labelText = @"您的網絡不給力!";        [hud hide: YES afterDelay: 2];    }];}@end

小結

此篇主要展示了一個典型的列表-->詳情場景的MVC實現,相關技術代碼可以直接用于自己的項目中.盡管這是簡化的場景,但依然可以很明顯地看出來數據,網絡請求與視圖間的相互調用,使代碼整體的可復用性大大降低! 而這,也是我們下次要用 MVVC 重構這個示例的核心目的之一!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 桂平市| 扶绥县| 筠连县| 梅河口市| 东明县| 哈巴河县| 遵义市| 邮箱| 峨眉山市| 罗田县| 河津市| 乳山市| 平利县| 宽城| 甘谷县| 柯坪县| 甘泉县| 德钦县| 陇西县| 平罗县| 仪陇县| 涪陵区| 乐亭县| 洛隆县| 莫力| 和静县| 维西| 诸城市| 吴川市| 南江县| 松滋市| 吕梁市| 东乌珠穆沁旗| 揭阳市| 元氏县| 洪洞县| 彰武县| 梨树县| 固始县| 安国市| 丽江市|