从互联网获取表格数据时,任何人都可以用 UItableview 来解释 MVC 吗?
Posted
技术标签:
【中文标题】从互联网获取表格数据时,任何人都可以用 UItableview 来解释 MVC 吗?【英文标题】:Can anybody explain MVC in terms of UItableview when getting data for the table from the internet? 【发布时间】:2013-10-29 09:21:55 【问题描述】:任何人都可以向我解释 MVC 在 UITableView 方面的工作原理,尤其是在从 Internet 获取数据时。
我很想知道 UItableview 的模型、视图和控制器是什么
我编写了以下 ViewController 代码,它从互联网获取数据并使用 AFNetworking 框架将其显示在表格上。
能否请您告诉我如何更改它并将其分成模型、视图和控制器。 我还写了一个刷新类,我猜它是模型的一部分。你能告诉我我是如何进行更改并使其成为模型的一部分吗?
编辑:下面的答案帮助我从理论上理解这个概念,有人可以帮助我相应地更改代码(通过编写一个关于如何将数组调用到此类并填充表的新类,因为我使用的是 json解析器)。我想实现它。而不仅仅是从理论上理解它。
#import "ViewController.h"
#import "AFNetworking.h"
@implementation ViewController
@synthesize tableView = _tableView, activityIndicatorView = _activityIndicatorView, movies = _movies;
- (void)viewDidLoad
[super viewDidLoad];
// Setting Up Table View
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tableView.hidden = YES;
[self.view addSubview:self.tableView];
// Setting Up Activity Indicator View
self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.activityIndicatorView.hidesWhenStopped = YES;
self.activityIndicatorView.center = self.view.center;
[self.view addSubview:self.activityIndicatorView];
[self.activityIndicatorView startAnimating];
// Initializing Data Source
self.movies = [[NSArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:@"http://itunes.apple.com/search?term=rocky&country=us&entity=movie"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:refreshControl];
[refreshControl endRefreshing];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
self.movies = [JSON objectForKey:@"results"];
[self.activityIndicatorView stopAnimating];
[self.tableView setHidden:NO];
[self.tableView reloadData];
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
NSLog(@"Request Failed with Error: %@, %@", error, error.userInfo);
];
[operation start];
- (void)refresh:(UIRefreshControl *)sender
NSURL *url = [[NSURL alloc] initWithString:@"http://itunes.apple.com/search?term=rambo&country=us&entity=movie"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
self.movies = [JSON objectForKey:@"results"];
[self.activityIndicatorView stopAnimating];
[self.tableView setHidden:NO];
[self.tableView reloadData];
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
NSLog(@"Request Failed with Error: %@, %@", error, error.userInfo);
];
[operation start];
[sender endRefreshing];
- (void)viewDidUnload
[super viewDidUnload];
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
return YES;
// Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
if (self.movies && self.movies.count)
return self.movies.count;
else
return 0;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
static NSString *cellID = @"Cell Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
NSDictionary *movie = [self.movies objectAtIndex:indexPath.row];
cell.textLabel.text = [movie objectForKey:@"trackName"];
cell.detailTextLabel.text = [movie objectForKey:@"artistName"];
NSURL *url = [[NSURL alloc] initWithString:[movie objectForKey:@"artworkUrl100"]];
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"placeholder"]];
return cell;
@end
【问题讨论】:
【参考方案1】:你问的问题很大。但让我尽可能简单地回答。
模型 - 您的数据源;最终是您的网络服务数据 控制器应该是拥有表视图的东西,它负责在视图上设置属性、对视图中的事件做出反应并根据需要对模型进行更改 视图 -- 表格视图和表格视图单元格的组合有很多方法可以在您的 Web 数据和表格视图之间进行协调,但我可能建议将您的 Web 服务调用重构为一个单独的商店类 - 例如 iTunesStore - 让该类负责进行调用到服务并使用结果设置内部数组,它还应该能够返回行数以及给定行索引的特定项目。
然后,您可以让此类响应对所需表视图委托方法的调用。其他要考虑的事情,使这个其他类成为单例,使其符合 UITableviewDatasource 协议本身并将其分配为表视图的数据源。
就像我说的,这是一个很大的问题,你有很多选择,但我已经为你提供了一些关于下一步要考虑的事情。
更新 我正在添加一些代码示例以帮助澄清。首先,我想明确表示我不会提供完整的解决方案,因为这样做会要求我对必要的实际解决方案做出过多的假设——并且因为有几种不同的方式可以与 AFNetworking 合作,网络服务,等等......而且我不想在那个兔子洞里被跟踪。 (例如在客户端缓存数据、后台任务和 GCD 等...)只是向您展示如何连接基础知识——但您肯定想学习如何在后台任务上使用 AFNetworking,查看 Core Data或 NSCoding 用于缓存,以及一些其他主题来正确地做这类事情。
只要说在适当的解决方案中就足够了: - 您不想同步调用您的网络服务 - 您也不希望每次都重新请求相同的数据 - 即不要从服务中重新下载相同的记录,除非它已更改 - 我没有在这里展示如何做这些事情,因为它超出了范围;查看下面的书籍推荐以及此链接以了解这些主题Ray Wenderlich - sync Core Data with a web service
对于您的数据服务代码,我将创建一个“存储”类。 (帮自己一个忙,如果您还没有 Big Nerd Ranch ios 书籍,请获取它。iOS Programming 4th Edition
在下面的代码中加一点盐 - 由于我无法进入的原因,我无法从我的 Mac(在 Win 机器上)执行此操作,我也无法复制或甚至将代码通过电子邮件发送给自己......所以我在 *** 编辑器中完成所有工作......
我的 iTunesStore 合同(头文件)如下所示:
// iTunesStore.h
#import <Foundation/Foundation.h>
@interface iTunesStore : NSObject
- (NSUInteger)recordCount;
- (NSDictionary*)recordAtIndex:(NSUInteger)index; // could be a more specialized record class
+ (instancetype)sharedStore; // singleton
@end
...实现看起来像这样:
// iTunesStore.m
#import "iTunesStore.h"
// class extension
@interface iTunesStore()
@property (nonatomic, strong) NSArray* records;
@end
@implementation iTunesStore
-(id)init
self = [super init];
if(self)
// DO NOT DO IT THIS WAY IN PRODUCTION
// ONLY FOR DIDACTIC PURPOSES - Read my other comments above
[self loadRecords];
return self;
- (NSUInteger)recordCount
return [self.records count];
- (NSDictionary*)recordAtIndex:(NSUInteger)index
NSDictionary* record = self.records[index];
-(void)loadRecords
// simulate loading records from service synchronously (ouch!)
// in production this should use GCD or NSOperationQue to
// load records asynchrononusly
NSInteger recordCount = 10;
NSMutableArray* tempRecords = [NSMutableArray arrayWithCapacity:recordCount];
// load some dummy records
for(NSInteger index = 0; index < recordCount; index++)
NSDictionary* record = @@"id": @(index), @"title":[NSString stringWithFormat:@"record %d", index];
[tempRecords addObject:record];
self.records = [tempRecords copy];
// create singleton instance
+ (instancetype)sharedStore
static dispatch_once_t onceToken;
static id _instance;
dispatch_once(&onceToken, ^
_instance = [[[self class] alloc] init];
);
return _instance;
@end
我现在有一个“存储”对象单例,我可以使用它来获取记录、返回给定记录并告诉我记录数。现在我可以从视图控制器中移动很多执行此操作的逻辑。
现在我不需要在您的 VC viewDidLoad 方法中执行此操作。理想情况下,您的存储对象中有一个异步方法来获取记录,并在加载记录后有一个块来回调您。在您重新加载记录的块内。类似“可能”的签名如下所示:
[[iTunesStore sharedStore] loadRecordsWithCompletion:^(NSError* error)
... if no error assume load records succeeded
... ensure we are on the correct thread
[self.tableView reloadData]; // will cause table to reload cells
];
您的视图控制器数据源方法现在如下所示:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection(NSInteger)section
[[iTunesStore sharedStore] recordCount];
在 cellForRowAtIndexPath 内部 - 我还调用我的 store 对象来获取正确的记录
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
// ... get cell
// ... get record
NSDictionary* record = [[iTunesStore sharedStore] recordAtIndex:indexPath.row];
// ... configure cell]
return cell;
这就是它的要点。如上所述,其他要做的事情是:
让 iTunesStore 实现 UITableViewDataSource,然后直接处理 tableview 数据源方法 - 如果您这样做,您不想让 iTunesStore 成为单例。您可以将 iTunesStore 的一个实例设置为 tableview 的委托,而不是 viewcontroller。这种方法有利也有弊。 我没有展示任何真正的异步行为或这个应用程序迫切需要的缓存 这确实展示了如何完成一些模型职责并分离一些 tableview 数据源问题。希望这将有助于为您提供一些关于您可能探索的不同方向的想法。 编码愉快!
【讨论】:
嗨 Bladebunny,你能用我的代码向我解释一下吗?通过编写一个新类来完成任务,因为我使用的是 json 解析器。【参考方案2】:就 UITableViewController 而言,通常所有角色模型、视图和控制器 (MVC) 都由您的 UITableViewController 本身扮演。您的代码也是如此。
-
作为模型 - 它为您的表格视图提供数据。
作为控制器 - 它控制表格的外观和感觉,如行数、部分、高度和宽度等,将数据从模型提供到表格视图
As View - 它的 view 属性包含 UITableView
现在,要采用不同的方法,您可以将模型从控制器类中分离出来。为此,有一个来自 NSObject 的子类,并让它设置它的状态,以便控制器使用。
希望这对你有意义。
【讨论】:
以上是关于从互联网获取表格数据时,任何人都可以用 UItableview 来解释 MVC 吗?的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 中从服务器数据库获取数据到 Sqlite 数据库