-1

アプリ内にローカルにある JSON ファイルから項目のリストが取り込まれた uitableview があります。リストをテーブルに取得し、選択 (または選択解除) すると nsmutablearray に保存される項目の複数選択まで、すべてが機能します。

問題は、ユーザーがビューを離れて戻ってきて、別の項目を選択する (または現在選択されている項目の選択を解除する) 場合です。この時点で、可変配列は空になります。

可変配列の nsuserdefaults 保存が問題であるかどうかはわかりません。それは問題なく保存されますが、ビューが再表示され(この時点では可変配列の値は問題ありません)、ユーザーがテーブル行に触れると、配列は再びnullになります。

私の.hファイル:

@interface CategoriesViewController : UITableViewController {

    NSMutableArray *_selectedItems;

    NSString *filePath;

    NSString *string;

}

// arForTable array will hold the JSON results from the api
@property (nonatomic, retain) NSArray *arForTable;

@property (nonatomic, retain) NSMutableArray *categorySelected;

@property (nonatomic, retain) NSString *jsonStringCategory;
@property(nonatomic, retain) UIView *accessoryView;

@end

私の.mファイル:

@implementation CategoriesViewController
@synthesize arForTable = _arForTable;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.categorySelected = [[NSMutableArray alloc] init];

    [self reloadMain];

    // assignment reference so don't release
     _selectedItems = [(AppDelegate *)[[UIApplication sharedApplication] delegate] selectedCategories];

    self.tableView.hidden = NO;

}

-(void) reloadMain {

    // countrySaved value from NSUserDefaults
    NSUserDefaults * defaults =  [NSUserDefaults standardUserDefaults];

    NSString *countryString = [defaults stringForKey:@"selectedCountryTableString"];
    NSString *cityString = [defaults stringForKey:@"selectedCityTableString"];
    NSLog(@"countrystring from category is %@", countryString);
    NSLog(@"citystring from category is %@", cityString);

    // getting path to the file

    if ([defaults stringForKey:@"selectedCountryTableString"] == NULL) {

        filePath = [[NSBundle mainBundle] pathForResource:@"categoriesit" ofType:@"json"];

    } else if ([countryString isEqualToString:@"UK"]) {

        filePath = [[NSBundle mainBundle] pathForResource:@"categoriesuk" ofType:@"json"];

    } else if ([countryString isEqualToString:@"Italy"]) {

        filePath = [[NSBundle mainBundle] pathForResource:@"categoriesit" ofType:@"json"];

    } else if ([countryString isEqualToString:@"Spain"]) {

        filePath = [[NSBundle mainBundle] pathForResource:@"categorieses" ofType:@"json"];

    } else if ([countryString isEqualToString:@"Brazil"]) {

        filePath = [[NSBundle mainBundle] pathForResource:@"categoriesbr" ofType:@"json"];
    }


    NSString *fileContent = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

    //NSLog(@"File content: %@", fileContent);

    // creating new parser
    SBJSON *parser = [[SBJSON alloc] init];

    // parsing the first level
    NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];
    NSDictionary *menu = (NSDictionary *) [data objectForKey:@"menu"];

#ifdef DEBUG
    NSLog(@"menu is %@",menu);
#endif

    NSMutableArray *itemsTMP = [[NSMutableArray alloc] init];

    NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:nil error:nil];

    // NSLog(@"results File test %@",dict);

    itemsTMP = [dict objectForKey:@"results"];
    // NSLog(@"itemsTMPitemsTMP File test %@",itemsTMP);

    self.arForTable = [itemsTMP copy];

    [self.tableView reloadData];

}

#pragma mark - Table view data source

- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.arForTable count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

        [cell.textLabel setFont:[UIFont fontWithName: @"Asap-Bold" size: 14.0f]];
        [cell.detailTextLabel setFont:[UIFont fontWithName: @"Asap-Bold" size: 14.0f]];

        cell.selectedBackgroundView = [[UIView alloc] initWithFrame:CGRectZero];

        cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRed:204.0/255.0 green:56.0/255.0 blue:55.0/255.0 alpha:1];

    }

    UIImageView *cellAccessoryImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon-tick.png"]] ;
    UIImageView *cellAccessoryNoneImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@""]] ;


    if([_selectedItems containsObject:indexPath]){
        cell.accessoryView = cellAccessoryImageView;
    } else {
        cell.accessoryView = cellAccessoryNoneImageView;
    }

    // Get item from tableData
    NSDictionary *item = (NSDictionary *)[_arForTable objectAtIndex:indexPath.row];

    // encoding fix

    NSString *correctStringTitle = [NSString stringWithCString:[[item objectForKey:@"key"] cStringUsingEncoding:NSISOLatin1StringEncoding] encoding:NSUTF8StringEncoding];

    cell.textLabel.text = [correctStringTitle capitalizedString];

    NSNumber *num = [item objectForKey:@"id"];

    cell.detailTextLabel.text = [num stringValue];

    cell.detailTextLabel.hidden = YES;

    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    NSUserDefaults * defaults =  [NSUserDefaults standardUserDefaults];

    if([_selectedItems containsObject:indexPath]){
        [_selectedItems removeObject:indexPath];

        [self.categorySelected removeObject:[[self.arForTable objectAtIndex:indexPath.row] objectForKey:@"id"]];

        string = [self.categorySelected componentsJoinedByString:@","];


        [defaults setObject:string forKey:@"selectedCategoryTableString"];

        NSLog(@"%@ defaults from did select remove categorySelected",[defaults stringForKey:@"selectedCategoryTableString"]);

        NSLog(@"%@ STRING FROM contains / removeObj",string);

    } else {

        [_selectedItems addObject:indexPath];

        [self.categorySelected addObject:[[self.arForTable objectAtIndex:indexPath.row] objectForKey:@"id"]];

        NSUserDefaults * defaults =  [NSUserDefaults standardUserDefaults];

        string = [self.categorySelected componentsJoinedByString:@","];

        [defaults setObject:string forKey:@"selectedCategoryTableString"];

        NSLog(@"%@ providerSelected from did select add ",self.categorySelected);

        NSLog(@"%@ STRING FROM contains / addObj",string);

    }

    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    //      [tableView reloadData];
}


-(void) viewWillAppear:(BOOL)animated {

    [super viewWillAppear:NO];

    [self.navigationController setNavigationBarHidden:YES animated:NO];

    self.navigationController.toolbarHidden = YES;

    NSUserDefaults * defaults =  [NSUserDefaults standardUserDefaults];

 //   NSLog(@"ALL DEFAULTS %@", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);

    NSLog(@"%@ defaults from view appear categorySelected",[defaults stringForKey:@"selectedCategoryTableString"]);

    string = [defaults stringForKey:@"selectedCategoryTableString"];


    NSLog(@"%@ STRING from will appear",string);

}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

.h にあるアプリ デリゲートでも:

@property (strong, nonatomic) NSMutableArray *selectedCategories;

そして.mで:

 `_selectedCategories = [NSMutableArray new];

didFinishLaunchingWithOptions:メソッドで

ただ明確にします:

ビューが再び表示されると (出力を nslog すると)、mutablearray が保存され、正しく取得されます。変更可能な配列は、テーブル行が再びタッチされたときにのみクリアされます。誰かがこれを手伝ってくれたらありがとう。私はしばらくそれに固執しています...

4

5 に答える 5

3

ユーザーが他のビューに移動して戻ってくるたび self.categorySelected = [[NSMutableArray alloc] init];に実行され、空の配列になります。

于 2013-02-21T14:11:08.517 に答える
3

ビューを離れるときに、最初に配列をシリアル化します。

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:@[@"1",@"2",@"3"]];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"myarray"];
[[NSUserDefaults standardUserDefaults] synchronize];

次に、そのビューに戻ったらデシリアライズします。

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"myarray"];
NSArray *myarray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"MYARRAY %@", myarray);

注: キーが見つからない場合は、新しい配列を初期化します。

于 2013-02-25T01:56:44.080 に答える
3

編集

// DONT EVER EVER EVER EVER EVER EVER DO THIS!!!
//  We don't use types as variable names, that is implicit...
//  I get it, this is a string, BUT WHAT IS IT A STRING OF, the name
//  'string' does you, and anyone else, no good.  Think about all your 
//  code like you are writing it for someone else, because when you come
//  back to it in 6 months, you will be someone else, and you won't know
//  what this means
NSString *string;

編集を終了

このように NSUserDefaults を使用することはありません。JSON はすでにアーカイブ可能なオブジェクト (NSMutableArray) に解析されています。viewDidLoad では、次のようなことを試してみてください。

-(void)viewDidLoad
{
     // Load the array from a plist file
     self.dataYouNeed = [NSMutableArray arrayWithContentsOfFile:@"someFileName.plist"];
     // If we got back nil, that file didn't exist, so call 'reloadMain',
     //  do your parsing there THEN SAVE to a plist using:
     //
     // [myArray writeToFile:@"someFileName.plist"]
     //
     if(self.dataYouNeed == nil) [self reloadMain];

     // Then do the exact same thing when you try to persist your selection...
     //  aka do not store a CSV string, just store an Array, and call writeToFile:
     //  when you want to save, and arrayWithContentsOfFile when you want to read 
     //  it back in

}

その上、データの取得元に応じて、すべてのデータを JSON ファイルから移動して plist に設定すると、すべての解析コードを破棄できます.... :)。基本的に私が言いたいのは、これは単純なタスクには少し複雑すぎるということです。自分の人生を楽にしてください。

編集

「self.string」を使用しないと問題が発生する可能性があります。単に「string」を参照するのは危険です。毎回新しい参照を作成しています。これにより、メモリ リークが発生する可能性が高くなります。(ARC は魔法ではありません。すべてのメモリ管理を処理することはできません)

編集

わかりました、コードを読み直して、いくつかのことに気付きました。

1. CSV 文字列を「string」インスタンス変数に保存するのはなぜですか?

これはやや冗長です。数行のコードで設定せずに、この変数から読み取ることは決してありません。メソッドのスコープ内で宣言された NSString である必要があります。

2.「_selectedItems」が AppDelegate の「selectedCategories」配列への参照を保持していることを期待していますか?

特に @property 宣言を行わない限り、この仮定を行うことはできません。ARC はそれを処理する方法を認識していないため、ビューを離れるときに参照を解放する可能性があります。より可能性が高いのは、その変数を設定するたびにメモリ リークが発生している可能性です。また、参照をリセットするために viewDidLoad が再度呼び出されることを保証することもできません。これはおそらく viewWillAppear で設定する必要があります。

3. nil 参照が発生している NSMutableArray はどれですか?

'_selectedItems' の場合は、#2 を検討してください。「categorySelected」の場合、これもおそらくこのビューが消えるときに解放されます。これが本当に永続化しようとしているものである場合、viewDidAppear メソッドからデータを入力しないのはなぜですか。viewDidAppear で行う唯一のことは、「文字列」変数を設定することです (#1 が言うように、実際には読み取られません)。ここで「categorySelected」を設定するつもりでしたか? NSUserDefaults からリストを取得し、配列を返すその文字列の componentsSeparatedByString: メソッドを使用して「categorySelected」に入力するつもりだったと思います

于 2013-02-25T21:52:56.720 に答える
2

ここで「保持」を「強い」に変更してみてください。

@property (nonatomic, retain) NSMutableArray *categorySelected;
于 2013-02-21T14:11:57.907 に答える
2

selectedItems問題は、メソッドで配列を設定していることだと思いますviewDidLoad。おそらく、viewDidLoad一度は機能しています。

viewWillAppearメソッドに次の行を追加するだけです。

_selectedItems = [(AppDelegate *)[[UIApplication sharedApplication] delegate] selectedCategories];
于 2013-03-01T12:14:25.750 に答える