luca590
luca590

Reputation: 470

UITableView not updating

I'm using a Core Data model and a UITableViewController table view. My model seems to be working quite well however my table view is not updating when I add an entity to my model. The reason I believe my model is working is because when I add an entity nothing shows up in the view during run time, however if I cut the task then start it up new, suddenly the entity I previously created shows up in the table view.

Here is my table view controller - AudioTableViewController.h

#import <UIKit/UIKit.h>
#import "Bank.h"
#import "Player.h"

@class Player;
@interface AudioTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
    Player *myMainMan;
}

-(void)addAudioEntityToArray:(AudioFile *)event;
-(NSMutableArray *)recordingsArray;

@end

AudioTableViewController.m (implementation file)

#import "AudioTableViewController.h"

@implementation AudioTableViewController

-(void)addAudioEntityToArray:(AudioFile *)event {
    NSIndexPath *indexPath;

    if(event.type) {
        [[MusikerViewController recordingsArray] addObject:event];//self?
        indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    }else {
        [[MusikerViewController downloadsArray] addObject:event];
        indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
    }

     [[self tableView] setEditing:YES animated:NO];
     [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                      withRowAnimation:UITableViewRowAnimationNone]; 
}


- (void)viewDidLoad {
      [[self tableView] reloadData];
      [super viewDidLoad];

      self.title = @"Audio Files";//put this in application delegate

      self.navigationItem.leftBarButtonItem = self.editButtonItem;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 2;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.    

    NSLog(@"Place I");

    if(section == 0) {
        return [[MusikerViewController recordingsArray] count];
    } else if (section == 1) {
        return [[MusikerViewController downloadsArray] count];
    }
}

... more methods

Here is part of my Bank class that might be relevant Bank.h

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "AudioFile.h"
#import "AudioTableViewController.h"
#import "MusikerAppDelegate.h"

@interface Bank : NSObject <UIAlertViewDelegate> {
    NSManagedObjectContext *managedObjectContext;
    AudioFile *sound;
}

@property (retain, nonatomic) NSManagedObjectContext   *managedObjectContext;

+(NSString *)getDataPath:(NSString *)fileExtDate;

-(AudioFile *)addAudioFileEntityToModelWithDate:(NSDate *)theD andURLString:(NSString *)str;
-(BOOL)removeAudioFromModel:(id)audio;
-(NSMutableArray *)getFetchArray;

@end

Bank.m

#import "Bank.h"

@implementation Bank
@synthesize managedObjectContext;

- (AudioFile *)addAudioFileEntityToModelWithDate:(NSDate *)theD andURLString:(NSString *)str {
     sound = (AudioFile *)[NSEntityDescription insertNewObjectForEntityForName:@"AudioFile" inManagedObjectContext:managedObjectContext];

     sound.creationDate = theD;
     sound.soundPath = str; //set the sound path to the sound file's url
     [self alertForTitle];

     return sound;
}

- (BOOL)saveContext {
    NSError *error = nil;
    if(!managedObjectContext) {
        NSLog(@"managedObejctContext problem at saveContext Bank");
    }

    if (![managedObjectContext save:&error]) {
        return NO;
    } else {
        return YES;
    }
}

- (NSMutableArray *)getFetchArray {

    NSLog(@"ManagedObjectContext at getFetchArray");
    NSLog(@"Context: %@",managedObjectContext);

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    if(!managedObjectContext) {
        NSLog(@"There is no managedObjectContext in getFetchArray Bank");
    }
       NSEntityDescription *entity = [NSEntityDescription entityForName:@"AudioFile" inManagedObjectContext:managedObjectContext];
    [request setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];
    [sortDescriptors release];
    [sortDescriptor release];

    NSError *error = nil;
    NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
    if (mutableFetchResults == nil) {
        NSLog(@"mutableFetchResults array is nil");
    } 
    [request release];
    return mutableFetchResults;
}

+ (NSString *)getDataPath:(NSString *)fileExtDate {

    NSLog(@"getDataPath called");

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    documentsDirectory = [documentsDirectory stringByAppendingPathComponent:@"Musik Directory"];         //changing the recording directory to Musik Directory

    NSError *error;
    if (![[NSFileManager defaultManager] fileExistsAtPath:documentsDirectory]) {
    [[NSFileManager defaultManager] createDirectoryAtPath:documentsDirectory withIntermediateDirectories:YES attributes:nil error:&error]; 
        NSLog(@"In the if statement");
    }

    NSString *docPath = [documentsDirectory stringByAppendingPathComponent:fileExtDate];

    return docPath;

}

Player.h

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "Bank.h"

@class Bank;
@interface Player : NSObject <AVAudioPlayerDelegate, AVAudioRecorderDelegate> {

Bank *daBank;

AVAudioPlayer *musicPlayer;
AVAudioRecorder *musicRecorder;

NSDate *myDate;
NSString *strPath;

NSString *documentPath_;
NSMutableArray *arrayListOfRecordSound;
}

-(void)playSoundFile:(NSString *)soundFilePath;
-(BOOL)saveRecordingWithDate:(NSDate *)theD andURLString:(NSString *)str;
-(void)recordSomething;
-(void)playRecording;
-(void)play;

+ (NSString *)dateString:(NSDate *)date;
+ (NSString *)dateString;

@end

Player.m

- (Bank *)daBank 
{    
    if(!daBank) {
        daBank = [[Bank alloc] init];
    }

    return daBank;
}

-(BOOL)saveRecording { //this is the method that adds the new object to both the //model and view, the method that calls this method is not shown
    Bank *B = [MusikerViewController daBank];
    AudioTableViewController *ATVC2 = [MusikerViewController ATVControl];
    AudioFile *myAudioFileMusicX314 = [[B addAudioFileEntityToModelWithDate:myDate andURLString:strPath] retain];

    myAudioFileMusicX314.type = true;

    [ATVC2 addAudioEntityToArray:myAudioFileMusicX314]; 

    if(![B saveContext]) { 
        NSLog(@"addAudioFileEntityToModel is returning a nil managedObjectContext");
        return NO;
    }

    [myDate release];
    [strPath release];
    [myAudioFileMusicX314 release];
[ATVC2 release];

    return YES;            
}

Last but not least, MusikViewController.h

#import <UIKit/UIKit.h>
#import "Player.h"
#import "Bank.h"
#import <QuartzCore/QuartzCore.h>
#import "MyButtonView.h"
#import "AudioTableViewController.h"

@class AudioTableViewController;
@class Bank;
@class Player;
@interface MusikerViewController : UIViewController {
]    BOOL *pressed;
}

Player *myMainMan;
Bank   *daBank;
AudioTableViewController *ATVControl;
NSMutableArray   *recordingsArray;
NSMutableArray   *downloadsArray;

+ (Bank *)daBank;
+ (Player *)myMainMan;
+ (AudioTableViewController *)ATVControl;
+ (NSMutableArray *)recordingsArray;
+ (NSMutableArray *)downloadsArray;

@end

MusikViewController.m

+ (NSMutableArray *)recordingsArray {
    NSLog(@"recordingsArray called");

    if(!recordingsArray) {
        recordingsArray = [[NSMutableArray alloc] init];
        NSMutableArray *bigTempArray = [[[[Bank alloc] init] autorelease] getFetchArray]; //change this
        for(AudioFile *af in bigTempArray)
            if(af.type) {
                [recordingsArray addObject:af];
            }
        NSLog(@"recordingsArray exists");
    }
    return recordingsArray;
}

+ (NSMutableArray *)downloadsArray {
    NSLog(@"recordingsArray called");

    if(!downloadsArray) {
        downloadsArray = [[NSMutableArray alloc] init];
        // if(!bigTempArray)
        NSMutableArray *bigTempArray = [[[[Bank alloc] init] autorelease] getFetchArray];
        for(AudioFile *af in bigTempArray)
            if(!af.type) {
                [downloadsArray addObject:af];
            }
    }
    return downloadsArray;
}

If there are any more methods that might be relevant, let me know and I will happily post them.

I should also mention that, I can add a single entity to the array after I launch the app, but after that its the same problem.

Upvotes: 6

Views: 3678

Answers (6)

Mike Pollard
Mike Pollard

Reputation: 10195

In your addAudioEntityToArray method your call to addObject will add the new event to the END of the array, not the beginning.

Therefor the indexPath that you need to send to insertRowsAtIndexPaths is: like [NSIndexPath indexPathForRow:[MusikerViewController recordingsArray].count-1 inSection:0]

Upvotes: 0

tharris
tharris

Reputation: 2242

Add [self.tableView reloadData] into addAudioEntityToArray method at the end. This will force the UITableView to reload from the model after each insertion.

Upvotes: 1

Mundi
Mundi

Reputation: 80265

I think your problem is the architecture of your view controllers. Try this:

1) Set up all the core data stack in the app delegate. Pass the associated managed object context to all your view controllers as a property. That is the pattern Apple uses in many of its sample code examples.

2) For each table view controller, create its own NSFetchedResultsController, normally also as a property. Use the Apple samples as a guide.

3) Never fetch any data from another view controller, as you do in your code. You cannot and should not get the table data for the AudioTableViewController from the MusikerViewController.

It is possible and sometimes useful to have a central data object that provides data to view controllers. You cannot have this data object be a view controller. This is a blatant (and in your case fatal) violation of the MVC design pattern. Your error most likely stems from that (because MusikerViewController is not on screen and may even be deallocated).

4) Make sure that in your datasource methods you use only the fetched results controller to populate the tables and deliver media.

Upvotes: 3

DogCoffee
DogCoffee

Reputation: 19946

I suggest using a fetchresultscontroller, I used arrays before, but much better with FRC.

Upvotes: 0

Armaan Stranger
Armaan Stranger

Reputation: 3130

May be your - (void)addAudioEntityToArray:(AudioFile *)event {} method is called from secondary thread (other than MainUi thread) so that directly reloading table will not work.

try instead

[self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];

Hope it Helps!!

Upvotes: 5

Tala
Tala

Reputation: 8928

You're setting eventsArray via setter:

[self setEventsArray:[daBank getFetchArray]];

Why don't you access it via getter? You might be using another instance of array

self.eventsArray or [self eventsArray]

Upvotes: 0

Related Questions