У меня есть приложение, состоящее из нескольких представлений, в которых я обрабатываю объекты Core Data, обычно используя NSTableViews, подключенные к контроллерам массива с помощью привязок самым простым и обычным способом. Однако одно из представлений не использует обычные привязки, потому что объекты этого объекта должны быть обработаны (некоторые строки используются для создания других строк) и представлены в NSTableView на основе представления, поэтому им требуется промежуточный массив для их упаковки для моих нужд. В графическом интерфейсе я использую привязки с «objectValue:key_name» (ключи из нового массива, состоящего из обработанных данных).
В других представлениях и сущностях моего приложения, когда я нажимаю значок на панели инструментов, отображается конкретное представление, а изменения графа объектов автоматически отображаются в табличных представлениях благодаря привязкам.
Когда я отображаю представление, связанное с конкретной обработкой, обновления явно не видны сразу, и я должен запустить (пустую) функцию, которая программно выполняет новую выборку, обрабатывает результат и обновляет массив, заполняющий табличное представление на основе представления.
Вот проблема: табличное представление правильно заполняется при запуске приложения, потому что я вызываю функцию в awakeFromNib. Если я хочу обновить представление таблицы, я могу сделать это, нажав кнопку, связанную с IBAction, которая вызывает функцию обновления. Это также работает. Но когда я возвращаюсь к представлению с помощью значка на панели инструментов, оно не работает, хотя вызов этого представления также вызывает функцию обновления (это означает, что в классе AppDelegate я вызываю функцию, написанную в конкретном классе контроллера представления ).
Сначала произошел сбой, и отладчик показал, что manageObjectContext равен нулю. Это неожиданно, потому что он работает правильно, когда функция вызывается из awakeFromNib или из IBAction. Поискав в сети объяснения, я обнаружил, что мне нужно было явно ссылаться на контекст управляемого объекта приложения. Итак, я добавил код для этого, и теперь он больше не падает, но ничего не делает. Функция по-прежнему отлично работает при вызове из awakeFormNib или из IBAction, но не имеет никакого эффекта при вызове диспетчером представлений через щелчок на панели инструментов.
Что я пропустил? Спасибо за вашу помощь. Вот код конкретного контроллера представления:
//////// OpplogViewController.h
#import <Cocoa/Cocoa.h>
#import "ManagingViewController.h"
@interface OpplogViewController : ManagingViewController {
IBOutlet NSTableView *opplogTableView;
IBOutlet NSArrayController *opplogController;
}
@property (strong) NSMutableArray *allOpps;
- (IBAction)refreshOpp:(id)sender;
- (void)fetchTasks:(NSString *)entityName;
@end
//////// OpplogViewController.m
#import "OpplogViewController.h"
@interface OpplogViewController ()
@end
@implementation OpplogViewController
@synthesize allOpps = _allOpps;
- (id)init {
self = [super initWithNibName:@"OpplogViewController" bundle:nil];
if (!self) {
return nil;
}
[self setTitle:NSLocalizedString(@"Opplogview", @"")];
_allOpps = [[NSMutableArray alloc] init];
return self;
}
- (void)awakeFromNib {
[self fetchTasks:@"Opplog"];
// Opplog is the database entity to be fetched
}
- (IBAction)refreshOpp:(id)sender {
[self fetchTasks:@"Opplog"];
}
// below is the function called to fetch data and update the table view
- (void)fetchTasks:(NSString *)entityName {
// below: 2 lines added to avoid crash because context is nil
id delegate = [[NSApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
// below, we reset the array used to populate the table view
[_allOpps removeAllObjects];
NSFetchRequest *requestOpp = [[NSFetchRequest alloc] init];
NSPredicate *predicate = ...some predicate... ;
[requestOpp setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self.managedObjectContext]];
[requestOpp setPredicate:predicate];
NSError *error = nil;
NSArray *fetchResult = [self.managedObjectContext executeFetchRequest:requestOpp error:&error];
for (id obj in fetchResult) {
// processing the data
// creating a dictionary with resulting data
NSDictionary *anOpp = ... ;
// populating the array with the dictionaries
[_allOpps addObject:anOpp];
}
[_allOpps sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:YES]]];
[opplogController setContent:_allOpps];
[opplogTableView reloadData];
}
@end
Это AppDelegate, который вызывает функцию обновления в классе контроллера представления Opplog, если мы вернемся к этому конкретному представлению:
////// AppDelegate.h
#import <Cocoa/Cocoa.h>
@class ManagingViewController;
@interface AppDelegate : NSObject <NSApplicationDelegate> {
NSMutableArray *viewControllers;
IBOutlet NSToolbar *toolBar;
}
@property (unsafe_unretained) IBOutlet NSWindow *window;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (IBAction)changeViewController:(id)sender;
- (void)displayViewController:(ManagingViewController *)vc;
////// AppDelegate.m
#import "AppDelegate.h"
#import "OpplogViewController.h"
// also importing the controller class header of the app's other views
// ...
@implementation AppDelegate
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize managedObjectContext = _managedObjectContext;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// nothing here, was generated by Xcode
}
-(id)init {
self = [super init];
viewControllers = [[NSMutableArray alloc] init];
ManagingViewController *vc;
vc = [[OpplogViewController alloc] init];
[vc setManagedObjectContext:[self managedObjectContext]];
[viewControllers addObject:vc];
// below, repeating the same as paragraph above, for each view of the app
// ...
return self;
}
-(void)awakeFromNib {
[self displayViewController:viewControllers[0]];
[toolBar setSelectedItemIdentifier:NSLocalizedString(@"ViewOpplog", @"")];
// because the Opplog view is the view which must be displayed on startup
}
-(void)displayViewController:(ManagingViewController *)vc {
BOOL ended = [_window makeFirstResponder:_window];
if (!ended) {
NSBeep();
return;
}
NSView *v = [vc view];
[_window setContentView:v];
}
-(IBAction)changeViewController:(id)sender {
// switching views is an opportunity to save the context
NSError *error = nil;
[[self managedObjectContext] save:&error];
// now managing the view switch using toolbar icons
// the sender is the clicked toolbar item
NSInteger i = [sender tag];
ManagingViewController *vc = viewControllers[i];
// We go back to the Opplog view using the first toolbar icon
// which has a tag=0 and we call the function that gives us trouble.
// This call is supposed to update the data on the Opplog view
// when it will be loaded.
if (i == 0) {
OpplogViewController *pendingController = [[OpplogViewController alloc] init];
[pendingController fetchTasks:@"Opplog"];
}
[self displayViewController:vc];
}
А это код классаManagingViewController.
////// ManagingViewController.h
#import <Cocoa/Cocoa.h>
@interface ManagingViewController : NSViewController {
NSManagedObjectContext *__strong managedObjectContext;
}
@property (strong) NSManagedObjectContext *managedObjectContext;
@end
////// ManagingViewController.m
#import "ManagingViewController.h"
@interface ManagingViewController ()
@end
@implementation ManagingViewController
@synthesize managedObjectContext;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
@end