NSPanel not showing. - objective-c

My approach to this may be all wrong so I appreciate your patience.
I have a button in my main XIB file linked to this method in my document.m file:
- (IBAction)showTagModal:(id)sender {
if (!_FileTagWindowController){
_FileTagWindowController = [[FileTagWindowController alloc]init];
}
[_FileTagWindowController showWindow:self];
}
_FileTagWindowController is declared as a property in document.h and using breakpoints when the method is called, as far as I can tell is initializing properly, however _windowNibName and _window remains nil.
FileTagWindowController.h looks like this.
#import <Cocoa/Cocoa.h>
#interface FileTagWindowController : NSWindowController{
}
#property (strong) IBOutlet NSArrayController *tagsArray;
- (IBAction)saveContext:(id)sender;
#end
FileTagWindowController.m looks like this:
#import "FileTagWindowController.h"
#interface FileTagWindowController ()
#end
#implementation FileTagWindowController
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
NSLog(#"Window Did Load!");
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (IBAction)saveContext:(id)sender {
}
#end
in my FileTagWindowController.xib I have File Owner set to FileTagWindowController as the custom class. I have the File Owner's "window" outlet linked to the window (NSPanel). That's all that should be required correct? The NSLOG statement in WindowDidLoad never gets called. I tried using [super initWithWindowNibName] in FileTagWindowController.m but that crashes not only the app, but Xcode as well with an endless initialization loop. Am I missing something obvious here?
Thanks all so much.

Try something like the following.
// document.h
#import "FileTagWindowController.h"
#property (strong) filetagWindowController *FileTagWindowController;
// document.m
#synthesize filetagWindowController;
- (IBAction)showTagModal:(id)sender {
if (self.filetagWindowController == nil) {
self.filetagWindowController = [[FileTagWindowController alloc] initWithWindowNibName:#"FileTagWindowController"];
}
[filetagWindowController showWindow:self];
[[filetagWindowController window] setReleasedWhenClosed:NO];
[NSApp runModalForWindow:filetagWindowController.window];
filetagWindowController = nil;
}
You may also want to call NSWindowWillCloseNotification to observe its state and see if filetagWindowController is closed.

Related

Modifying string content in NSTextView works under viewDidLoad method, but not under myMethod

I am trying to update the contents of an NSTextView that is connected to myViewController as a referencing outlet to the Files Owner which is the subclass myViewController.
When I use an IBAction from a button, or use the viewDidLoad method of the controller, I can update the text fine. However, when I try run the method from another class (referred to in this example as anotherViewController), it runs the method, but the textview does not change.
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
self.outText.string = #"I am updated text! I also work!";
}
- (void)updateTextView:(NSString *)argText {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
- (void)updateTextViewWithoutArg {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
#end
In anotherViewController.m , which has all the relevant imports, I call this:
myViewController *viewtask = [[myViewController alloc] init];
[viewtask updateTextViewWithoutArg];
Nothing happens. The method runs and logs that it should have updated, but no text updates. I have tried many different approaches, including textstorage and scrollrange methods, they all work the already working sections, but make no difference in the sections not working.
I've also tried just for fun:
myViewController *viewtask;
[viewtask updateTextViewWithoutArg];
Also using the instance variable _outText
Also using [self.outText setString:#"string"];
Also using [_outText setString:#"string"];
Again, they work but only in the already working sections.
This should be simple but isn't logical to me. In swift all I need to do is
self.outText.string = "I update whenever I'm called!"
Views you create in Interface Builder are lazily created, so if you access them before viewDidLoad is called they are nil.
If your case, calling
myViewController *viewtask = [[myViewController alloc] init];
does not cause the views to be created so when you call
[viewtask updateTextViewWithoutArg];
self.outText is nil.
You can see that this is what is happening by updating your code as below:
- (void)updateTextView:(NSString *)argText {
NSAssert(self.outText != nil, #"self.outText must not be nil");
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
you should see the assert fire.
I appear to have found a solution by making myViewController a singleton class and using sharedInstance. For this particlar app, myViewController is a debug output window and will never need to be placed in another view.
I won't accept this answer yet, as it's not the best one I'm sure. There may still be a proper solution presented that allows finding the applicable myViewController instance, and modifying the outText property attached to it. Using this singleton makes subclassing tedious as I would have to make a new class for every instance if I wanted to be able to address say 10 View Controllers.
Anyway - the way I've been able to satisfy my simple requirement:
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
+ (id)sharedInstance;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
static myViewController *sharedInstance = nil;
+ (myViewController *)sharedInstance {
static myViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[myViewController alloc] init];
});
return sharedInstance;
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedInstance = self;
}
- (void)viewDidUnload {
sharedInstance = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
sharedInstance.outText.string = #"Button Pressed";
}
- (void)updateTextView:(NSString *)argText {
sharedInstance.outText.string = argText;
}
- (void)updateTextViewWithoutArg {
sharedInstance.outText.string = #"I make it to the TextView now";
}
#end
Now when I use this code from within anotherViewController.m it updates the right instance:
[myViewController.sharedInstance updateTextView:#"Updating with this string"];

NSTabView blocking file-drop events active on underlying Window.

I have successfully implemented a file-drop functionality in my app. The Application window has a few NSTabView objects where dropping on them does not work. Anywhere else in the window the file-drop works fine.
I have tried to make the app delegate a delegate for the NSTabView, but this did not help.
Anyone have a setup for the NSTabView not to filter out the drop-actions so the whole window can be transparent to the file-drop actions ?
For a more generic solution than olekeh's I made it IB friendly so you can hook it up to any object that complies with the NSDraggingDestination protocol.
#import <Cocoa/Cocoa.h>
#interface DropFilesView : NSView
#property (nullable, assign) IBOutlet id<NSDraggingDestination> dropDelegate;
#end
#implementation DropFilesView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
-(void) awakeFromNib {
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType,
(NSString *)kPasteboardTypeFileURLPromise,kUTTypeData, NSURLPboardType, nil]]; //kUTTypeData
[super awakeFromNib];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender{
return [self.dropDelegate draggingEntered:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
return [self.dropDelegate performDragOperation:sender];
}
#end
I found the solution to this !! - I am posting it here for others who might need.
The NSTabView object has for each of its tabs an NSTabViwItem.
Under each of those, there is a regular NSView - that I subclassed with the following code: - The code assumes that you already have "draggingEntered" and "performDragOperation" in your AppDelegate as this class just forwards these messages to the app delegate. You will also need to put the declarations for those methods in you AppDelegate.h
// DropFilesView.h
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface DropFilesView : NSView
#end
and the implementation:
// DropFilesView.m
#import "DropFilesView.h"
#implementation DropFilesView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
-(void) awakeFromNib {
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType,
(NSString *)kPasteboardTypeFileURLPromise,kUTTypeData, NSURLPboardType, nil]]; //kUTTypeData
[super awakeFromNib];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
AppDelegate* del = [AppDelegate sharedAppDelegate];
return [del draggingEntered:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
AppDelegate* del = [AppDelegate sharedAppDelegate];
return [del performDragOperation:sender];
}
#end
In Interfacebuilder, I set the new class for all the NSView objects covering areas where drop does not work, to this new one.
A similar approach can be used for NSImageView and the WebView classes. However, for the last one, do not use [super awakeFromNib] to prevent the default drag-and drop handling for the web view object.

Opening a sheet on 10.9

In one of my controllers I'd like to open a modal sheet on 10.9.
This is the implementation code for the first controller:
#import "ABSAdvancedPreferencesViewController.h"
#import "ABSUnsyncableWindowController.h"
#interface ABSAdvancedPreferencesViewController ()
#property (strong, nonatomic) ABSUnsyncableWindowController *unsyncableWindowController;
#end
#implementation ABSAdvancedPreferencesViewController
- (id)init {
return [super initWithNibName:#"ABSAdvancedPreferencesViewController" bundle:nil];
}
- (IBAction)showUnsyncableSheet:(id)sender {
if (self.unsyncableWindowController == nil) {
self.unsyncableWindowController = [ABSUnsyncableWindowController new];
}
[self.view.window beginSheet:[self.unsyncableWindowController window] completionHandler:^(NSModalResponse returnCode) {
CLS_LOG(#"Table dismissed");
}];
}
When I execute the linked IBAction nothing happens. The NSWindowController subclass that should should show the modal has a XIB, Visible at launch is deactivated and window is already an outlet.
Debugging I saw that the window parameter is nil here, presumably because I call new in the previous controller:
#implementation ABSUnsyncableWindowController
- (id)initWithWindow:(NSWindow *)window {
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
Is there something else that I can check to show the modal sheet?
your ABSAdvancedPreferencesViewController initmethod is missing assigning the super result to self. Also, why don't you alloc/init the sheet controller as expected?
self.unsyncableWindowController = [[ABSUnsyncableWindowController alloc] initWithWindowNibName:#"XIBNAME"];
That should alloc the window controller and you should be able to access its window for display.

Delegate - How to Use?

I think I understand the logic behind a delegate. I got more the problem to use it. How many steps are involved? Do I have to use existing delegates? Or can I use my one ones?
In my example I got the AppDelegate that created many views (Objects / View Controllers) of the same type. Each view should somehow call a method on the AppDelegate to close itself. This would happen when a button within the view is touched. The method call would include the reference of the view (self).
So far I know from other languages responders, event listeners and so on. They are so simple to use.
Can anybody help me. I just found massive examples with a lot of code in the web. It can't be that hard to just call a parent in Objective C.
I think you should use for this the NSNotificationCenter
in you AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(buttonPushed:) name:#"ButtonPushedNotification" object:nil];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
...
...
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
this is the selector it will be called when the notification happens (we are still in the AppDelegate.m)
- (void)buttonPushed:(NSNotification *)notification {
NSLog(#"the button pushed...");
}
and in the ViewController.m when the button pushed (inside the method), you should post a notification like this:
{
...
[[NSNotificationCenter defaultCenter] postNotificationName:#"ButtonPushedNotification" object:nil];
...
}
You can create your own:
In MyView1.h:
#class MyView1;
#protocol MyView1Delegate <NSObject>
- (void)closeMyView1:(MyView1 *)myView1;
#end
#interface MyView1 : NSObject
{
id<MyView1Delegate> _delegate;
}
#property (assign, nonatomic, readwrite) id<MyView1Delegate> delegate;
...
#end
In MyView1.m:
#interface MyView1
#synthesize delegate = _delegate;
...
// The method that tells the delegate to close me
- (void)closeMe
{
....
if ([_delegate respondsToSelector:#selector(closeMyView1:)])
{
[_delegate closeMyView1:self];
}
}
#end
In AppDelegate.h:
#import "MyView1.h"
#interface AppDelegate <MyView1Delegate>
{
MyView1 *_myView1;
}
...
#end
In AppDelegate.m:
- (void)someCreateViewMethod
{
_myView1 = [[MyView1 alloc] initWithFrame:NSMakeRect(0, 0, 100, 200)];
[_myView1 setDelegate:self];
...
}
An easy way to get what you want is to just start with one view. Then, have each other view be presented modally. When the button in the view is pressed do
[self dismissModalViewControllerAnimated:YES];
And here's something I made a while ago when I was starting iPhone development that might help you with delegates
Delegates
//In parent .m file:
//assign the delegate
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segueName"])
{
childController *foo = segue.destinationViewController;
foo.delegate = self;
}
}
//implement protocol method(s):
- (void) methodName:(dataType*) dataName
{
//An example of what you could do if your data was an NSDate
buttonLabel.titleLabel.text = [[date description] substringToIndex:10];
}
//In parent .h file:
//import child header
#import "ChildName.h"
//indicate conformity with protocol
#interface ParentName : UIViewController <ChildNameDelegate>
//In child .h file
//declare protocol
#protocol ChildNameDelegate
- (void) methodName:(dataType*) dataName;
#end
//declare delegate
#property (unsafe_unretained, nonatomic) id<ChildNameDelegate> delegate;
//In child .m file
//synthesize delegate
#synthesize delegate;
//use method
- (IBAction)actionName:(id)sender
{
[delegate methodName:assignedData];
}

Show new window from status menu [duplicate]

OK, what am I doing wrong?
1. Created cocoa app and appDelegate named: window2AppDelegate
2. window2AppDelegate.h
#import "PrefWindowController.h"
#interface window2AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
PrefWindowController * ctrl;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction) buttonClick:(id)sender;
- (IBAction) buttonCloseClick:(id)sender;
#end
3. in xib editor, window connected to window controller - set to appdelegate, buttonclick actions to buttons
4, created
#import <Cocoa/Cocoa.h>
#interface PrefWindowController : NSWindowController {
#private
}
#end
#import "PrefWindowController.h"
#implementation PrefWindowController
- (id)init {
self = [super initWithWindowNibName: #"PrefWindow"];
return self;
}
- (void)dealloc {
// Clean-up code here.
[super dealloc];
}
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
#end
5. created new xib file named PrefWindow window IBOutlet connected to window from its controller (also controller set to PrefWindowController) Option "Visible At Launch" UNCHECKED! i want to see this window on buttonclick.
6. implemented window2AppDelegate
#import "window2AppDelegate.h"
#implementation window2AppDelegate
#synthesize window;
- (id) init {
if ((self = [super init])) {
ctrl = [[PrefWindowController alloc] init];
if ([ctrl window] == nil)
NSLog(#"Seems the window is nil!\n");
}
return self;
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return YES;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (IBAction) buttonClick:(id)sender {
// [[ctrl window] makeKeyAndOrderFront:self]; this doesn't work too :(
NSLog(#"it is here");
[ctrl showWindow:sender];
}
- (IBAction) buttonCloseClick:(id)sender {
[window close];
}
#end
7. After I build and run app: closebutton closes the app but buttonclick - won't show me PrefWindow!? Why and what am i doing wrong? Don't dell me that to show other window in cocoa objective-c is more difficult than in "stupid" Java or C#?
Finally i've managed the problem! In the nib editor for PrefWindow I had to do: Set File's owner class to: NSWindowController then connect window IBOutlet from File's owner to my (preferneces) window. After 6 days of unsuccessful attempts, google works.
Anyway, thanks for all your responses and time!
I'd suggest you move the creation of the PrefWindowController to applicationDidFinishLaunching:
I am not sure the application delegate's init method is called. Probably only initWithCoder: gets called when unarchiving the object from the NIB.
- (id) init {
if ((self = [super init])) {
ctrl = [[PrefWindowController alloc] init];
if ([ctrl window] == nil)
NSLog(#"Seems the window is nil!\n");
}
return self;
}
init is way too early in the scheme of things to be trying to test IBOutlets. They will still be nil yet. Not until later on in the object creation process will the nib outlets be "hooked up". The standard method where you can know that everything in the nib file has been hooked up is:
- (void)awakeFromNib {
}
At that point, all of your IBOutlets should be valid (provided they're not purposely referencing an object in a separate, yet-unloaded nib).
If PrefWindowController is a class that will only be used after the user chooses Preferences from the app menu, I would have to disagree with the others and say that I would not create the instance of the PrefsWindowController at all during the initial load. (Your main controller should be able to function independently from the prefs window). If you have a method that is meant to load the preferences window, then when that method is called, you should check to see if the PrefsWindowController instance is nil, and if it is, create it, then proceed to show the prefs window.