谁能向我解释一下 MVC 在 UITableView 方面是如何工作的,尤其是在从互联网获取数据时。
我非常想知道当涉及到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
这是一个很大的问题。但让我通过使其尽可能简单来回答。
- 模型 - 数据源;最终是 Web 服务数据
- 控制器应该是拥有表视图并调解在视图上设置属性并对视图中的事件做出反应并根据需要对模型
进行更改
的东西 - 视图 -- 表格视图和表格视图单元格
的组合
方法可以在您的 Web 数据和表视图之间进行协调,但我可能会建议将 Web 服务调用重构到一个单独的存储类中 - 比如 iTunesStore - 让该类负责调用服务并使用结果设置内部数组, 它还应该能够返回行计数以及给定行索引的特定项目。
然后,让此类响应对所需表视图委托方法的调用。 要考虑的其他事项,使这个其他类成为单一实例,使其符合 UITableview数据源协议本身,并将其分配为表视图的数据源。
就像我说的,这是一个大问题,有很多选择给你,但我给了你一些关于下一步去哪里考虑的事情。
更新
我添加了一些代码示例来帮助澄清。 首先,我想明确表示,我不打算提供整体解决方案,因为这样做需要我在必要的实际解决方案方面承担太多假设 - 并且因为有几种不同的方法可以使用AFNetworking,Web服务等。而且我不想在那个兔子洞里被跟踪。 (例如在客户端上缓存数据,后台任务和GCD等)只是向您展示如何连接基础知识 - 但您肯定想学习如何在后台任务上使用AFNetworking,查看Core Data或NSCoding以进行缓存,以及其他一些主题来正确执行此类操作。
适当的解决方案中,只需说明这一点:
- 您不想同步
调用您的 Web 服务 - 您也不想每次都重新请求相同的数据 - 即不要从服务重新下载相同的记录,除非它发生了变化
- 我在这里没有展示如何做这些事情,因为它超出了范围;查看下面的书籍推荐以及此链接,以了解这些主题 雷·温德利希 - 将核心数据与 Web 服务同步
对于您的数据服务代码,我将创建一个"存储"类。 (帮自己一个忙,如果你还没有大书牧场iOS书,那就去买它。
iOS 编程第 4 版
对以下代码持保留态度 - 由于我无法进入的原因,我无法从我的 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];
}
Inside cellForRowAtIndexPath - 我还调用我的存储对象来获取正确的记录
- (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的实例设置为表视图的委托,而不是视图控制器。 这种方法有利有弊。
- 我没有显示任何真正的异步行为或缓存,这个应用程序正在 呼吁
- 这确实显示了如何完成一些模型职责并分离一些表视图数据源问题。
希望这将有助于为您提供一些关于您可能探索的不同方向的想法。 祝您编码愉快!
就UITableViewController而言,通常所有角色Model,View和Controller(MVC)都由UITableViewController本身扮演。您的代码也是如此。
- 作为模型 - 它向表视图提供数据。
- 作为控制器 - 它控制表格的外观和感觉,如行数、部分、行的高度和宽度等,提供从模型到表格视图的数据
- 作为视图 - 其视图属性保存 UITableView
现在,要采用不同的方法,您可以将 Model 与控制器类分离出来。为此,有一个来自NSObject的子类,并让它设置其状态,该状态可以被控制器使用。
希望这对你有意义。