Имам приложение, направено от няколко изгледа, на които боравя с обекти с основни данни, обикновено използвайки NSTableViews, свързани с контролери на масиви, използващи свързвания по най-простия и обичаен начин. Един от изгледите обаче не използва обичайните обвързвания, тъй като обектите на този обект трябва да бъдат обработени (някои низове, използвани за създаване на други низове) и представени в базиран на изглед NSTableView, така че те изискват междинен масив, който да ги пакетира за моите нужди. В графичния интерфейс използвам свързвания с "objectValue:key_name" (ключове от новия масив, съставен от обработени данни).
С другите изгледи и обекти на моето приложение, когато щракна върху икона в лентата с инструменти, се представя конкретен изглед и модификациите на обектната графика се виждат автоматично в изгледите на таблицата благодарение на свързванията.
Когато покажа изгледа, свързан с конкретната обработка, актуализациите очевидно не се виждат веднага и трябва да стартирам функция (void), която програмно извършва ново извличане, обработва резултата и актуализира масива, попълващ изгледа на таблица, базиран на изглед.
Ето го проблемът: табличният изглед се попълва правилно при стартиране на приложението, защото извиквам функцията в awakeFromNib. Ако искам да актуализирам табличния изглед, мога да го направя, като щракна върху бутон, свързан с IBAction, който извиква функцията за актуализиране. Работи също. Но когато се върна към изгледа с помощта на иконата на лентата с инструменти, това не работи, въпреки че извикването на този изглед също извиква функцията за актуализиране (което означава, че в класа AppDelegate извиквам функцията, написана в конкретния клас на контролера на изгледа ).
Първоначално се срина и дебъгерът разкри, че managedObjectContext е нула. Това е изненада, защото работи правилно, когато функцията се извика от 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