使用 NSUserdefaults 填充 UITableView

Posted

技术标签:

【中文标题】使用 NSUserdefaults 填充 UITableView【英文标题】:Filling UITableView using NSUserdefaults 【发布时间】:2018-09-30 17:11:54 【问题描述】:

我已经尝试了一段时间来让 tableview 正常工作。每天我都在取得一些进步,但我无法通过测试应用程序看到任何反馈,所以我不确定我做的事情是否正确。

我在谷歌上搜索了一段时间,查看了苹果文档,但我仍然不明白如何正确填充 UITableView。

第一个问题。我在哪里得到细胞?我需要制作一个自定义单元格来显示数据。解决方案似乎是使用 nib,但如何将其加载到另一个视图控制器上?

第二个问题。这个单元格显然应该有动态内容。此内容将从用户默认值中获取(我可以这样做),但我不知道如何访问笔尖中的标签来实现这一点。

第三个问题。我想要不同的部分(每个部分都有不同类型的单元格显示),具有不同的页眉/页脚。与往常一样,在这里我发现缺少文档。我怎样才能做到这一点?

第四个也是最重要的问题。我不知道如何加载单元格。显然这是由cellForRowAtIndexPath 完成的,但我不确定它是如何做到的。

最后,这是代码(连同堆栈跟踪)。希望您能够帮助我。 请不要因为重复而关闭它。首先,它不应该是,其次,如果重复的问题是在 3 年前提出的,因为库已经更新了多少,它不是重复的。

#import "profileViewController.h"
#import "../Objs/CCProfileObject.h"

@interface profileViewController ()
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) IBOutlet UITableViewCell *proxySampleCell;
@property (strong, nonatomic) IBOutlet UITableViewCell *ccSampleCell;
@end

@implementation profileViewController
@synthesize tableView;

- (void)viewDidLoad
    // Dark mode, you can skip all of this
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(changeColor:)
                                                 name:@"darkToggle" object:nil];

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    if([userDefaults boolForKey:@"darkMode"] == YES)
        [self changeColor:nil];
    



- (void)viewDidAppear:(BOOL)animated

    // Display profiles
    NSData *arrayData = [[NSUserDefaults standardUserDefaults] objectForKey:@"profileArray"];
    NSArray *profiles = [NSKeyedUnarchiver unarchiveObjectWithData:arrayData];

    for(NSObject *profile in profiles)
        if([profile class] == [CCProfileObject class])
        
            CCProfileObject *ccProfile = (CCProfileObject*) profile;
            printf("%s\n", [ccProfile.getName UTF8String]);
            // Retrieve info from the object
            // Duplicate cell
            // Change appropriate labels
            // Add cell to a specific section
        
        // Different types of cells will be added later
        // Concept is the same, what changes is the specific section and cell to duplicate

    



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    if(indexPath.section == 1)
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reusableShippingCell"];
        if (cell == nil) 
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"reusableShippingCell"];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
        

        cell.textLabel.text = @"aldo";

        return cell;
    



    return nil;

// This should be the only part that actually works (I use the same class twice because proxy and shipping are not yet done)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    // Display profiles
    NSData *arrayData = [[NSUserDefaults standardUserDefaults] objectForKey:@"profileArray"];
    NSArray *profiles = [NSKeyedUnarchiver unarchiveObjectWithData:arrayData];
    NSMutableArray *sectionData = [[NSMutableArray alloc]init];

    for(NSObject *profile in profiles)
        // Credit
        if([profile class] == [CCProfileObject class])
        
            [sectionData insertObject:@1 atIndex:0];
        
        // Shipping
        else if([profile class] == [UIImage class])
        
            [sectionData insertObject:@1 atIndex:1];
        
        // Proxy
        else if([profile class] == [UIImage class])
        
            [sectionData insertObject:@1 atIndex:2];
        
    

    int toReturn = 0;
    for(int i = 0; i < [sectionData count]; i++)
        toReturn += [[sectionData objectAtIndex:i] integerValue];
    
    return toReturn;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    // Credit card section
    if(section == 0)
        return 1;
     else if (section == 1)
        return 2;
     else 
        return 3;
    


`Assertion failure in -[UITableView _configureCellForDisplay:forIndexPath:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3698.52.10/UITableView.m:9453
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView (<UITableView: 0x16106d000; frame = (0 0; 375 812); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x1d0243bd0>; layer = <CALayer: 0x1d0030f60>; contentOffset: 0, -88; contentSize: 375, 117; adjustedContentInset: 88, 0, 83, 0>) failed to obtain a cell from its dataSource (<profileViewController: 0x15ff14c70>)
First throw call stack:
(0x1837f6d8c 0x1829b05ec 0x1837f6bf8 0x1841e6fa0 0x18d4975b0 0x18d4941e8 0x18d493e00 0x18d492b1c 0x18d48e668 0x18d3cb770 0x18796d25c 0x1879713ec 0x1878ddaa0 0x1879055d0 0x18d7b56b4 0x18379f2bc 0x18379ea7c 0x18379c7b0 0x1836bcda8 0x18569f020 0x18d69d78c 0x1048a4f90 0x18314dfc0)
libc++abi.dylib: terminating with uncaught exception of type NSException

你不需要用一个答案来解决我的所有问题。即使解决问题/解释我需要做的事情就足够了,因为这至少可以让我向前迈进一点。

【问题讨论】:

在 Objective-C 中使用表格视图的例子不胜枚举。 3 年前的答案在今天和当时一样有效。多年来,表格视图的填充方式没有任何变化。从 Apple 自己的文档开始:Table View Programming Guide for ios 查看 Apple 提供的众多示例应用之一:UITableView Fundamentals for iOS @rmaddy 似乎已经过时了。例如,我在“cellForRowAtIndexPath”中找不到[self configureCell: forIndexPath: 什么是过时的?我给了你两个有很多页面的链接。称为configureCell 的东西将是添加到视图控制器的自定义代码。 好的。对不起。目前正在筛选代码,希望我能找到我需要的东西。谢谢。 【参考方案1】:

首先,您不需要为UITableViewCell 定制笔尖,除非您有一个非常复杂的单元格。相反,您可以使用UITableViewAttributes inspector 中的Prototype Cells 直接设计单元格布局。只需给Prototype Cells 一个数字并在UITableView 视图中设计适当的单元格布局。不要忘记为它们中的每一个设置identifier,这将帮助您在cellForRowAtIndexPath 中加载正确的布局。

对于简单的单元格布局,您可以为单元格的每个元素提供一个tag 编号。这将帮助您在需要时访问它们,如下所示:

UILabel *label = [cell viewWithTag:100];

每个UITableView都需要一个DataSource,你将实现UITableViewDataSource来提供:

    表格的部分数 每个部分的项目数 单个单元格的内容

您只是在提供的源代码中执行了此操作,除了一件事:cellForRowAtIndexPath 需要返回 UITableViewCell,而不是 nil。如果返回nil,会抛出异常。

所以,一个有效的cellForRowAtIndexPath 可能看起来像这样:

@implementation ViewController 
    NSArray *data;


- (void)viewDidLoad 
    [super viewDidLoad];

    // You load your data from NSUserDefault here
    data = [self loadDataFromNSUserDefault];


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    NSString *cellIdentifier = @"defaultCell";
    if (indexPath.section == 0) 
        cellIdentifier = @"customCell1";
     else if (indexPath.section == 1) 
        cellIdentifier = @"customCell2";
    

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    // Access your view's children using their `tag`
    UILabel *title = [cell viewWithTag:100];
    UIImageView *icon = [cell viewWithTag:101];

    // Set content
    title.text = [[data objectAtIndex:indexPath.row] objectForKey:@"title"];
    icon.image = [UIImage imageNamed:[[data objectAtIndex:indexPath.row] objectForKey:@"icon"]];

    return cell;

【讨论】:

【参考方案2】:

您的核心问题在于这一行 - if (indexPath.section == 1) - 部分编号从 0 开始。因此,您返回nil 以获取第一部分(编号为 0)中的单元格,以及第二部分(编号为 1)之外的任何部分,这会导致崩溃。你应该永远不要cellForRow返回nil

话虽如此 - 我仍然建议您阅读 cmets 中提供的链接,您仍然可以学到一些有价值和/或有趣的东西。

【讨论】:

以上是关于使用 NSUserdefaults 填充 UITableView的主要内容,如果未能解决你的问题,请参考以下文章

使用 NSUserDefaults 保存 NSMutableArray 不起作用

(Swift) 将数组存储和检索到 NSUserDefaults

如何使用 NSSet 填充 tableView? (迅速)

TableView 中的 UICollectionView 不填充单元格?

停止调用 cellForRowAtIndexPath

TableView 不显示数据