Cocoa Multiple Window Controllers - objective-c

I have a Cocoa app that uses automatic reference counting and does not use core-data (not document-based) and I want to be able to create multiple instances of a window I have defined in a nib file.
I currently have this in my AppDelegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
// for slight performance bump, we assume that users usually have 1 session open.
sessionWindowControllers = [[NSMutableArray alloc] initWithCapacity:1];
}
- (void) awakeFromNib {
// on start, we create a new window
[self newSessionWindow:nil];
}
- (IBAction)newSessionWindow:(id)sender {
SessionWindowController *session = [[SessionWindowController alloc] initWithWindowNibName:#"SessionWindow"];
//add it to the array of controllers
[sessionWindowControllers addObject:session];
[session showWindow:self];
}
SessionWindowController is a subclass of NSWindowController.
but when I run it, I get the runtime error
LayoutManagement[30415] : kCGErrorIllegalArgument:
_CGSFindSharedWindow: WID 11845 Jun 8 18:18:05 system-process LayoutManagement[30415] : kCGErrorFailure: Set a breakpoint #
CGErrorBreakpoint() to catch errors as they are logged. Jun 8
18:18:05 system-process LayoutManagement[30415] :
kCGErrorIllegalArgument: CGSOrderFrontConditionally: Invalid window
Is using NSMutableArray even a good way to manage multiple windows, or is there a better design pattern? Thanks!

Ans. kindly provided by Ben Flynn:
I put the
sessionWindowControllers = [[NSMutableArray alloc] initWithCapacity:1];
in applicationDidFinishLaunching, but since awakeFromNib is called first, we are trying to add an instance of session to the array before it even exists.
Solution: put array init inside the awakeFromNib before we make our first window.
- (void) awakeFromNib {
sessionWindowControllers = [[NSMutableArray alloc] initWithCapacity:1];
[self newSessionWindow:nil];
}

Related

Open ears text to speech (voice) not working when getting string from another class/controller (IOS, Objective c)

I am very new to objective c and OpenEars so please forgive me if I have some messy code and if I am lost in very simple problem.
Anyhow, I have two controllers in this application. The first being the default ViewController and the second one being a new one that I made called ReplyManagerController.
The code in the ViewController basically uses the one in the tutorial with some (maybe more some) changes.
EDIT:
The app is supposed to be a basic app where a user says something and the app replies.
But the original problem was that I could not get the string to display or TTS to work when my ViewController got it's string from another class/controller.
The answer in my below mentions that it was probably because my other class was calling my ViewController without the self.fliteController initialized.
How would I initialize the ViewController with self.fliteController initialized?
ViewController.m
- (void) pocketsphinxDidReceiveHypothesis:(NSString *)hypothesis recognitionScore:(NSString *)recognitionScore utteranceID:(NSString *)utteranceID {
NSString *strResult = hypothesis; //speech to text to string
ReplyManager* replyObject = [[ReplyManager alloc] init];
[replyObject speechResult:(NSString*)strResult viewController:self];
}
- (void) getReply:(NSString*)replyStr{
[self.fliteController say:replyStr withVoice:self.slt];
[self updateText:replyStr];
}
- (IBAction)updateText:(NSString*)replyStr{
labelOne.text = replyStr;
labelOne.adjustsFontSizeToFitWidth = YES;
labelOne.minimumFontSize = 0;
}
Any help will be great! Thanks!
ReplyManager.m
- (void) speechResult:(NSString*)strResult {
NSString *replystr;
NSString *greetings = #"Hi!";
NSString *sorry = #"Sorry I didn't catch that?";
ViewController* getReply = [[ViewController alloc] init];
if ([strResult isEqualToString:#"HELLO"])
{
replystr = greetings;
[getReply getReply:(NSString*)replystr];
}
else
{
replystr = sorry;
[getReply getReply:(NSString*)replystr];
}
}
EDIT 2:
viewDidLoad Method
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.fliteController = [[OEFliteController alloc] init];
self.slt = [[Slt alloc] init];
self.openEarsEventsObserver = [[OEEventsObserver alloc] init];
[self.openEarsEventsObserver setDelegate:self];
}
ViewController* getReply = [[ViewController alloc] init];
Here you init a new ViewController which does not have self.fliteController defined most likely. You need to reuse previos controller, for example like this:
[replyObject speechResult:(NSString*)strResult viewController:self];
So you can use already initialized viewController later. Overall it is better to initialize objects like viewController or replyController beforehand, not inside callback methods.
It sounds like a timing issue where you're trying to use fliteController before it's been initialized.
In your ViewController class, where do you assign a value to the fliteController property? In an initializer? -(void)viewDidLoad?
In ReplyManagerController add:
ViewController* getReply = [[ViewController alloc] init];
// Add these lines
NSLog(getReply.fliteController); // Is this nil?
[getReply.view layoutIfNeeded];
NSLog(getReply.fliteController); // Is it still nil?
Does the above fix the problem? If so, you're probably initializing fliteController in -(void)viewDidLoad. What's the result of the two NSLog statements?

Magical Record object for current thread

I'm using Magical Record 2.3.0 beta 5 and I have troubles understanding how to get my NSManagedObjects for the current thread. I have a long running NSOperation where I need my PSPlayer (NSManagedObject).
When I init the NSOperation, I keep an id of my PSPlayer and re-fetch the same object in the operation's main method. According to Apple that the way to do it.
#implementation TAPlayerUpdateOperation
- (instancetype)initWithPlayer:(PSPlayer *)player;
{
self = [super init];
if (self) {
self.playerMD5Id = player.md5Id;
}
}
- (void)main
{
#autoreleasepool {
__block BOOL keepUpdating = YES;
PSPlayer *player = [[PSPlayer MR_findAllWithPredicate:[NSPredicate predicateWithFormat:#"md5Id == %#", self.playerMD5Id]] firstObject];
NSLog(#"player.md5Id = %#", player.md5Id);
// rest of my operation logic
}
}
#end
When I run my app with -com.apple.CoreData.ConcurrencyDebug 1, I get a crash when accessing the property in the NSLog statement.
What is the correct way to get my NSManagedObject so that it is safe for the current thread?
I've pinned the problem down to the following snippet where it crashes as well.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
PSPlayer *player =[[PSPlayer MR_findAll] firstObject];
NSLog(#"player = %#", player.name);
});
cheers,
Jan
You need to ensure that everything is saved and merged before the fetch would work. If you're using MR then it's better to take the managed object and call inContext: on it supplying the other context and have it do the work (it also avoids a predicate).
I expect the crash is because you use player.md5Id instead of self.playerMD5Id so you're accessinh the managed object on the wrong thread.

iOS: Issue Importing Calls From Another File

I'm still new to Objective-C so I'm having a hard time with this. In my AppController, When a user clicks on one of the ads in my app, then closes the ad and returns to my app, I would like to destroy and recreate the ad (Long story as to why). For some reason though, my code isn't working. There are no errors or warnings, but it doesn't do what I intended it to do. This is what my code looks like:
#import "MoPubManager.h"
......
- (void)applicationWillEnterForeground:(UIApplication *)application
{
MoPubManager *obj = [[MoPubManager alloc] init];
if( obj.adView ) {
[[MoPubManager sharedManager] destroyBanner];
}
[obj.adView refreshAd];
}
_adView, destroy banner, and refresh ad are both in the MoPubManager file, so as you can see I imported the file and turned MoPubManager into an object. (obj.adView was originally just _adView in MoPubManager.h, but I had to switch it to obj.adView to avoid warnings.) I may just be using the wrong calls, I would post the MoPubManager.mm file where the calls originally are but its a full page or two
In the line:
MoPubManager *obj = [[MoPubManager alloc] init];
You are creating a new instance of the MoPubManager class.
Next you are checking whether the adView property of that newly instantiated class is not nil. Unless adView gets populated in the init method of MoPubManager, this will always be nil, so the destroyBanner method will not be called.
However, the destroyBanner method is being called on a sharedManager... indicating a singleton pattern in use. Therefore you should not be creating a new instance as this goes against the reason for using a singleton. (You only ever have one instance of a class when using a singleton - see this for more info)
Without seeing more code, it seems that you should be calling something like:
if( [MoPubManager sharedManager].adView ) {
[[MoPubManager sharedManager] destroyBanner];
}
[[MoPubManager sharedManager].adView refreshAd];
First, what subclass is MoPubManager... It compiles fine? The problem here seems to be that you are creating a new MoPubManager instance, but you are using a singleton to destroy it, and they don't have the same reference. You should use something diferent like:
MoPubManager *obj = [[MoPubManager alloc] init];
if( [MoPubManager sharedManager].adView ) {
[[MoPubManager sharedManager] destroyBanner];
}
[[MoPubManager sharedManager].adView refreshAd];
You are not using correctly the singleton pattern.
try this , maybe will help you
- (void)applicationWillEnterForeground:(UIApplication *)application
{
MoPubManager *obj = [MoPubManager sharedManager];
if( obj.adView ) {
[obj destroyBanner];
}
[obj.adView refreshAd];
}

xcode with IBAction

I’m trying to make an app in xcode. I have made a class called myApp.m and .h
In my .m I have these lines of code
- (void)loadApp
AlarmItem *item1 = [[[AlarmItem alloc] initWithTitle:#"TEST2"] autorelease];
NSMutableArray *items = [NSMutableArray arrayWithObjects:item1, nil];
RootViewController *rootController = (RootViewController *) [navigationController.viewControllers objectAtIndex:0];
rootController.items = items;
and in my RootViewController I have an this method:
- (IBAction)RefreshMyApp:(id)sender {
MyApp *myApp2 = [[[MyAppalloc] init] autorelease];
[myApp2 loadData];
}
What I’m trying to do is calling the method from the myApp class and displayed in the tableView, but I always get an empty cell.
Any help is appreciated.
Is this meant to get you your UIApplication singleton? (i'm guessing MyAppalloc is a typo and should be MyApp alloc)
MyApp *myApp2 = [[[MyApp alloc] init] autorelease];
if so then you should be doing it like this:
MyApp *myApp2 = (MyApp*)[UIApplication sharedApplication];
If this is not the case you need to make it clearer what MyApp is (your app delegate?)
I guess, your application running in singleton instance, if this is something kind of NSView or a particular control you want to refresh, you could call its particular refresh method like,
[NSTableView reload];
[NSTextField setString];
etc...

How do you populate a NSArrayController with CoreData rows programmatically?

After several hours/days of searching and diving into example projects I've concluded that I need to just ask. If I bind the assetsView (IKImageBrowserView) directly to an IB instance of NSArrayController everything works just fine.
- (void) awakeFromNib
{
library = [[NSArrayController alloc] init];
[library setManagedObjectContext:[[NSApp delegate] managedObjectContext]];
[library setEntityName:#"Asset"];
NSLog(#"%#", [library arrangedObjects]);
NSLog(#"%#", [library content]);
[assetsView setDataSource:library];
[assetsView reloadData];
}
Both NSLogs are empty. I know I'm missing something... I just don't know what. The goal is to eventually allow multiple instances of this view's "library" filtered programmatically with a predicate. For now I'm just trying to have it display all of the rows for the "Asset" entity.
Addition: If I create the NSArrayController in IB and then try to log [library arrangedObjects] or manually set the data source for assetsView I get the same empty results. Like I said earlier, if I bind library.arrangedObjects to assetsView.content (IKImageBrowserView) in IB - with same managed object context and same entity name set by IB - everything works as expected.
- (void) awakeFromNib
{
// library = [[NSArrayController alloc] init];
// [library setManagedObjectContext:[[NSApp delegate] managedObjectContext]];
// [library setEntityName:#"Asset"];
NSLog(#"%#", [library arrangedObjects]);
NSLog(#"%#", [library content]);
[assetsView setDataSource:library];
[assetsView reloadData];
}
I was running into a similar situation where the (IKImageBrowserView) was not initializing even though the ArrayController would ultimately sync up with the NSManagedObjectContext.
Ultimately found this passage in the core data programming guide
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdBindings.html#//apple_ref/doc/uid/TP40004194-SW3
if the "automatically prepares content" flag (see, for example,
setAutomaticallyPreparesContent:) is set for a controller, the controller's initial content
is fetched from its managed object context using the controller's current fetch predicate. It
is important to note that the controller's fetch is executed as a delayed operation performed
after its managed object context is set (by nib loading)—this therefore happens after
awakeFromNib and windowControllerDidLoadNib:. This can create a problem if you want to
perform an operation with the contents of an object controller in either of these methods,
since the controller's content is nil. You can work around this by executing the fetch
"manually" with fetchWithRequest:merge:error:.
- (void)windowControllerDidLoadNib:(NSWindowController *) windowController
{
[super windowControllerDidLoadNib:windowController];
NSError *error = nil;
BOOL ok = [arrayController fetchWithRequest:nil merge:NO error:&error];
// ...
It looks like the problem is that you have not actually told the NSArrayController to fetch anything. NSArrayControllers are empty until you add objects either through bindings or manually.
After setting up library try to call its fetch method:
[library fetch:self];
Also, you probably know this already but it is possible to set bindings in code with the following method:
- (void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
Can also be added in awakeFromNib if subclassing the NSArrayCotroller or via your view controller
-(void)awakeFromNib
{
[self fetchWithRequest:nil merge:NO error:nil];
...
}