草庐IT

ios - 从互联网获取表数据时,有人可以用UItableview来解释MVC吗?

coder 2023-07-30 原文

任何人都可以向我解释关于UITableView的MVC的工作原理,尤其是从互联网获取数据时。

我非常想知道关于UItableview的模型, View 和 Controller 是什么

我编写了以下ViewController代码,该代码从Internet上获取数据,并使用AFNetworking框架将其显示在表上。

您能否告诉我如何进行更改并将其分为模型, View 和 Controller 。
我还编写了一个刷新类,我猜这是该模型的一部分。您能告诉我如何精确地进行更改并将其作为模型的一部分。

编辑:下面的答案从理论上帮助我理解了这个概念,有人可以帮助我相应地更改代码(通过编写有关如何将数组调用到此类并填充表的新类,因为我使用的是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服务数据
  • Controller 应该是拥有表 View 的事物,并介导 View 上的设置属性,并对 View 中的事件使用react,并根据需要对模型
  • 进行更改
  • View -表格 View 和表格 View 单元格的组合

  • 有很多方法可以在Web数据和表 View 之间进行协调,但我可能建议您将Web服务调用重构为一个单独的商店类(例如iTunesStore),让该类负责对服务的调用并设置带有结果的内部数组,它还应该能够返回行数以及给定行索引的特定项。

    然后,让此类响应所需的表 View 委托(delegate)方法的调用。要考虑的其他事项,使该另一个类成为单例,使其符合UITableviewDatasource协议(protocol)本身并将其分配为表 View 的数据源。

    就像我说的那样,这个大问题为您提供了很多选择,但是我给了您一些下一步的考虑。

    更新
    我添加一些代码示例以帮助阐明。首先,我想明确地说,我不会提供整体解决方案,因为这样做会要求我在必要的实际解决方案方面承担过多的责任,并且因为有几种不同的方法可以使用AFNetworking ,网络服务等。。。我不想被跟踪到那个兔子洞里。 (例如将数据缓存在客户端,后台任务和GCD等上。)只是向您展示了如何连接基础知识-但您一定会想学习如何在后台任务上使用AFNetworking,并研究Core Data。或NSCoding进行缓存,以及其他一些主题来正确地进行此类操作。

    只需说出一个适当的解决方案即可:
    -您不想同步调用Web服务
    -您也不想每次都重新请求相同的数据-即不要从服务中重新下载相同的记录,除非其更改了
    -我在这里没有展示如何做这些事情,因为它超出了范围;在下面查看书中的建议以及此链接,以获取有关这些主题的想法Ray Wenderlich - sync Core Data with a web service

    对于您的数据服务代码,我将创建一个“存储”类。 (请帮个忙,如果您还没有的话,请阅读《 Big Nerd Ranch iOS》一书。
    iOS Programming 4th Edition

    仔细阅读下面的代码-由于我无法进入的原因,我无法从Mac(在Win机器上)执行此操作,也无法复制甚至通过电子邮件通过电子邮件发送自己的代码。 ..所以我要在StackOverflow编辑器中做所有...

    我的iTunesStore契约(Contract)(头文件)如下所示:
    // 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
    

    我现在有一个“存储”对象单例,可以用来获取记录,返回给定记录以及告诉我记录数。现在,我可以从 View Controller 中移出很多执行此操作的逻辑。

    现在,我不需要在您的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
    }];
    

    您的 View Controller 数据源方法现在看起来像:
     - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection(NSInteger)section {
          [[iTunesStore sharedStore] recordCount];
      }
    

    在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的一个实例设置为表 View 的委托(delegate),而不是viewcontroller。这种方法有利有弊。
  • 我还没有显示任何真正的异步行为或缓存,此应用程序正在为
  • 呐喊
  • 这确实显示了如何实现您的某些模型职责以及如何分离一些tableview数据源关注点。

  • 希望这将帮助您对可能探索的不同方向提供一些想法。
    祝您编码愉快!

    关于ios - 从互联网获取表数据时,有人可以用UItableview来解释MVC吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19485119/

    有关ios - 从互联网获取表数据时,有人可以用UItableview来解释MVC吗?的更多相关文章

    1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

      使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

    4. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

      查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

    5. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

      我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

    6. ruby - 如何验证 IO.copy_stream 是否成功 - 2

      这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

    7. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

      我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

    8. ruby - Ruby 有 `Pair` 数据类型吗? - 2

      有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

    9. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

      我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

    10. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

      我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

    随机推荐