NSStatusItem with NSPopover and NSTextField - objective-c

I have a NSStatusItem that displays a NSPopover which contains a NSTextField but the text field isn't editable although it has been so be so in Xcode. Now this is a known bug and there is apparently a solution someone posted here. I really need to work around this bug.
I'll just quote the answer here for convenience:
The main problem is the way keyboard events works. Although the NSTextField (and all his superviews) receives keyboard events, he doesn't make any action. That happens because the view where the popover is atached, is in a window which can't become a key window. You can't access that window in no way, at least I couldn't. So the solution is override the method canBecomeKeyWindow for every NSWindow in our application using a category.
NSWindow+canBecomeKeyWindow.h
#interface NSWindow (canBecomeKeyWindow)
#end
NSWindow+canBecomeKeyWindow.m
#implementation NSWindow (canBecomeKeyWindow)
//This is to fix a bug with 10.7 where an NSPopover with a text field cannot be edited if its parent window won't become key
//The pragma statements disable the corresponding warning for overriding an already-implemented method
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (BOOL)canBecomeKeyWindow
{
return YES;
}
#pragma clang diagnostic pop
#end
That makes the popover fully resposive. If you need another window which must respond NO to canBecomeKeyWindow, you can always make a subclass.
I don't really understand what I am supposed to do. Do I just create these two files NSWindow+canBecomeKeyWindow (.h and .m) and that will do it? Because it doesn't work for me.
I am not sure about this but does this only work if I am actually using an NSWindow that displays the NSPopover? I am not using a NSWindow, how can I get the NSTextField to be editable?
Thanks.

Yes, just add the NSWindow+canBecomeKeyWindow (.h and .m) files to your project, and it should work. I'm using this technique in an app I'm currently developing, and it works fine. Make sure NSWindow+canBecomeKeyWindow.m is listed under "Compile Sources" in your project's Build Phases.
As an aside, I'm having other issues using NSPopover to show a window from an NSStatusItem. I haven't actually tried using it in my project, but this looks promising as an alternative.

Related

How can I delegate one NSViewController from another NSViewController in ObjC on Mac? [duplicate]

Hi I've seen this question asked a few times already but with no definite answer yet so I created it for xcode 7 and swift2 (which may have changed things a bit anyway).
I created a project using Xcode 7 and Cocoa OSX Story boards + swift2, so my project started with a NSWindowController that Connects to a NSViewController (as expected!). I added a NSToolbar to my window controller and added a NSButton to the toolbar. I changed my NSViewController to be one of the new NSSplitViewController that links to three NSViewControllers and displays their views horizontally - with vertical dividers - (similar to the layout you see in the photo app or pages in Yosemite +). My final goal will be that the button in My toolbar shows and hides the first split.
It is my understanding is, and I would expect that to achieve this I should create an action in the NSSplitViewController that changes the auto layout constrains more or less in the way they are working it out here: How to do collapse and expand view in mac application?.
And then somehow link this action to the NSButton that is in the Toolbar... which happens to be in the NSWindowController (far up and isolated in the hierarchy)...
I have already gone through other questions about NSToolbar and storyboards and failed to accomplish my goal:
The YouTube video: Cocoa Programming L17 - NSToolbar which is the closest I found to solve the problem, but his method does not work for storyboards, only creating your own xib file.
In this question: How to use NSToolBar in Xcode 6 and Storyboard? One person proposes to make the link using the first reponder and expecting everything to hook up at run-time (which looks a bit dodgy and not the way apple would implement it I think...). A second person suggested to create a view controller variable in the NSWindowController and manipulate its properties from there... but again, a bit dodgy too.
One latest comment I saw in that question which seems the best way to tackle the problem (but still not as good as I guess it could be) is to add a NSObjectController to the dock of each scene and when the scene loads, set the values of the objects to the other secene's controller. Is this really the best way to go ahead? If so, how could I achieve this one?
Apple did mention (again) in WWDC15 that they created storyboards for osx and the split-view controller that owns view-controllers so that you can move your logic and work to the specific view-controller, so I would be expecting to do everything from inside my split-view controller as this is the target that needs to change.
Does anyone know how to achieve this from the view controller itself? I really haven't been able to find a way to connect my ToolBarItem to it.
OK, I've created this question quite a few days ago and no answer so far so I've answer with what I recently did to overcome the problem.
After I created my Xcode project I did this:
Created a subclass MySplitViewController for the NSSplitViewController
Added an IBOutlet for each NSSplitViewItem. For example:
#IBOutlet weak var mySplitViewItem: NSSplitViewItem!
Created a subclass WindowController for the NSWindowController
Added an IBAction in the WindowController class that links to the NSToolbarItem (my button)
Added a property that gets the Window Controller's content as MySplitViewController
var mySplitViewController: MySplitViewController {
return self.window?.contentViewController as! MySplitViewController
}
Now I can access the split view controller's property from the Window Controller in the action I created:
mySplitViewController. mySplitViewItem.collapsed = true
I created some sample code that does this (but using a view controller and changing the text for a label here, just in case someone wants to see a working project with this behaviour. And a blog post about it too :)
One person proposes to make the link using the first reponder and expecting everything to hook up at run-time (which looks a bit dodgy and not the way apple would implement it I think...).
I think this first responder method is actually the proper way.
As an example:
Add something similar to the following, in whichever view controller makes sense.
#IBAction func doSomething(_ sender: AnyObject?) {
print("Do something.")
}
This will magically show up in the first responder:
In your storyboard, right-click the orange "first responder" icon above your window controller, and you should see doSomething in the very long list. You just need to connect that up to your toolbar button.
In the following screen capture, you can see my "Toggle Sidebar" button is connected to the toggleSidebar action in my first responder.
I didn't even have to write this method — it's provided by NSSplitViewController:
#IBAction open func toggleSidebar(_ sender: Any?)
So, I was working this same issue and finding no solution as you experienced. I read your post and was trying to figure how I would implement your solution when it occurred to me to use a notification. In about 30 seconds, I had a perfectly fine working solution:
In your windowController add an IBAction to post a notification like so
-(IBAction)toggleMasterViewClicked:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:nil];
}
Hook up that action to your NSToolbarItem, then in the viewController add self as an observer for that notification like so
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(toggleMasterView:) name:#"TestNotification" object:nil];
In your case, selector would be updateMyLabelText
I don't really see any downside here. No reference to other objects needed, no dependancies. Works flawlessly for me
While connectiong IBActions works by using either the First Responder or by adding an "Object" to the scene, then changing its class to the window's view controller class, this doesn't help with IBOutlets and delegates that you'd like to point to the view controller.
Here's a work-around for that:
Add the Toolbar to the View Controller, not to its Window. That way, you can make all the IBOutlet connections in the View Controller Scene easily. I've done that for years and found no issues with it, even when using Tabs.
You'll have to assign the window's toolbar in code, then. E.g. like this:
#interface ViewController ()
#property (weak) IBOutlet NSToolbar *toolbar; // connect this in your storyboard to the Toolbar that you moved to the View Controller Scene
#end
- (void)viewWillAppear {
[super viewWillAppear];
self.view.window.toolbar = self.toolbar;
}

Setting the Keyboard property in iPad

Ok, I'm in the process of developing my first iPad application. Yea I'm a newbie. I'm running into a number of problems implementing properties even though I'm using the exact same code as I used for the iPhone. I thought the two platforms were (are) the same iOS? For example, I can not set the Keyboard type for a UITextField either directly through Storyboard or programmatically. Here's what I've done:
storyboard
keyboard = number pad
programmatically
header
IBOutlet UITextField *txtValue;
#property (nonatomic, retain) UITextField *txtValue;
implementation
#synthesize txtValue;
txtValue.keyboardType = UIKeyboardTypeNumberPad;
I also tried:
[txtValue setKeyboardType:UIKeyboardTypeNumberPad];
Also there is an active IBOutlet for the UITextField between the ViewController screen and ViewController file in the Connections Inspector and the User Interaction enabled is checked.
There's absolutely nothing wrong with the default keyboard other than the fact that it's not what I want. Any idea what is going on here? Thanks...
I think I know what the answer is and it's pretty dumb. The answer is, there only is one keyboard on an iPad. The property only sets the configuration when it is opened. So if you set the Keyboard to NumberPad, the same keyboard opens up with the numbers showing. Not sure I like this.
That keyboard is not available on the iPad.
If you do not like the standard keyboard, I'd recommend you look into creating your own custom keyboard. Here is a document to help get you started:
http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/InputViews/InputViews.html#//apple_ref/doc/uid/TP40009542-CH12-SW2
All things considered, a custom iOS keyboard isn't as hard as it sounds. It's just another custom view. The hardest part is making some graphics to make it look the way you want.
If the custom route is not what you're looking for, there are also several "custom" keyboard people have already made. Checkout github or CocoaControls. Here is just one example of a custom number pad someone's made for the iPad:
https://github.com/azu/NumericKeypad
NumberPad is not a supported type on iPad.
(command/apple click on the UIKeyboardTypeNumberPad to see more about it)

Xcode: how to reset. No longer able to create outlet for newly added control in XIB

I have several XIBs following this pattern: a View Controller subclass containing a View subclass. I put my controls on that view.
Just now, I tried to add a new control to the view. I can do this, but I'm unable to connect the new control to an outlet. The assistant editor won't display the view subclass header either, though it will for a short while if I restart Xcode. Some other XIBs I have don't have the view controller object but instead have File's Owner set to a view controller subclass. They show the same problem.
Here is the Objects bar in IB:
"Matrix Editor View Controller" is a UIViewController subclass. This shows up fine in the Assistant Editor's "Counterparts" menu and I can drag from UIControls to it to create outlets & actions.
"Popup Cell View" is a UIView subclass. This is where the majority of my outlets and actions live. Previously this would show up in the Assistant Editor "Counterparts" menu and I could drag between it and my controls and the code quite happily. Now, whenever I add a new control in to this view, I can't drag from the Popup Cell View object to the new control, nor can I drag from the new control to the Assistant Editor window if it's showing the Popup Cell View header.
I recently had to upgrade to Lion (and hence Xcode 4.2.1 build 4D502); the last time I added controls to my XIBs was under Snow Leopard with the latest Xcode on that OS.
I believe that Xcode's caches or some internal state are out of whack. I have tried:
Restarting Xcode
A clean
Removing and re-adding the view subclass from the project
Deleting the DerivedData folder's contents from ~/Library/Developer/Xcode
And various combinations thereof. What else can I do to force Xcode to re-scan everything and allow me to connect up my controls to outlets?
Edit: I have noticed that I can create outlets in the View Controller for my new controls; I'm just not able to do this on the View subclass itself which is where I want them. I already have many on there. Could my new version of Xcode really be telling me that I'm not allowed to do this? Surely not. The Assistant Editor's Automatic mode changes every minute or so between two counterparts (the VC .h and .m) and four (the VC .h/.m and the View .h/.m).
Edit: I was able to connect an outlet, but only via this convoluted method:
Force the assistant editor to open my View subclass
Manually typing in an outlet for my new control
Dragging from the outlet in the assistant editor to the control itself
So something is very clearly broken. How can I fix it?
Very frustrating!
Thanks
When I have this problem, it is usually due to a mismatch in the class type somewhere. Verify that the class name of the object in you xib matched the class name in the interface and the implemetation files.
I'm a newb - I recognize this is a kludge, but hope this helps somebody. Sounds kinda sorta like the problem described above.
Suddenly, xcode refused to "insert new action outlets" into my AppDelegate.m (using control+drag, from a newly added xib element, into the class #implementation). Also, Xcode does not display that little-dark-circle next to each IBAction method in the .m file, like it used to.
Xcode allows drag of the blue-line from the 'source xib element' -- but would not show the "Insert Outlet or Action" & blue-circle-line with destination-highlight at the 'target line of insertion', in the .m file.
However, I can insert new outlet into my AppDelegate.h, using that above-described blue-line drag-method. That file is adorned with the cute little-black-circles, properly.
The kludge-around I came to was this
add 'bogus #implementation code' to the AppDelegate.h, as per:
#if 1
#implementation AppDelegate
#end
#endif
control+drag from the new xib element into/inside that #if 1 #implementation region in the .h
viola! xcode now displays the desired "Insert Action" & blue-circle-line target at the position for insertion. I can assign the method name and express whatever semantics I wish. Xcode source-editor shows a cute little-dark-circle to the left of the 'autogenerated' -(IBAction) line. For example, I added imanewxibobject as per:
#if 1
#implementation AppDelegate
- (IBAction)imanewxibobject:(id)sender {
printf("%s: hi there!\n",\_\_FUNCTION\_\_);
}
#end
#endif
(1+2)=3. Copy & paste that -(IBAction)imanewxibobject:... function into my actual AppDelegate.m (where it really belongs).
(2+2)=4. Turn that #if 1 (in the AppDelegate.h) to #if 0
(3+2)=5. recompile. Kablammo, the new UI element is alive with semantics!
I tried all: cleaning the project, deleting the files from the project, etc.
I peered into that .xib file and found it contained lots of what appears to be cross-referenced identifier indices, kinda what you may expect to see, in a file of this sort.
So I still don't have those little-dark-circles beside my -(IBAction)s -- but at least the code compiles, and I see the correct method-label string, for the Xcode Utilities->"Show Connections Inspector", with respect to the new UI element.
Anybody know how I can restore those cute little-dark-circles beside my "-(IBAction)"s? I find them very cute, and wish to have them back. I don't see any "Display cute-little-dark-circles" menu options. Bummer. Maybe in the next Xcode?

Window-less Cocoa application

I'm a complete beginner in Objective-C and Cocoa.
I would like to create a window-less application, which just shows a NSStatusItem in the system tray. The tray works fine, however, there is one problem.
For some reason my application automatically creates a window for me, which I do not want.
I thought it was caused by the automatic Interface Builder template created when I created the application in Xcode, so I deleted the .nib file from the project. However the window still gets created.
The only lines that contain a reference to the window are in the header:
NSWindow *window;
#property (assign) IBOutlet NSWindow *window;
and in the implementation file:
#synthesize window;
Both were added automatically, I did not write this.
How do I just stop the app from creating a window? I even tried to removing all references to window from the code, including the NSWindow *window, but the window still got created.
My temporary fix right now is to call [window close]; in the application, but surely there is a better way?
My suspicion is that nothing in your code is creating the window. When you create a new Cocoa Xcode application, Xcode sets up an XIB with your interface for you. Open up MainMenu.xib (should be under Resources) in interface builder and delete the window that it throws in by default.
If you don't want to show a window you may consider run your application in background. That will stop the window to appear.
In order to run your application in the background, set YES to "Application is background only" in your app's PLIST file
Return false in your NSDocument subclass' windowNibName method.

iOS Text View Not Updating

I've got a UITextView defined by
#property (nonatomic, retain) IBOutlet UITextView *quote;
in my view controller header, and I can set the value by using
quote.text = #"some text";
but the view doens't want to update the value, what can I do
Setting the text should immediately cause the UITextView to render your text under normal conditions.
Are you sure your that:
The UITextView is placed appropriately in your Nib and is visible?
The UITextView is appropriately linked with your outlet in your file owner (aka view controller)?
A quick test to verify the visibility of your UITextView - just place some sample text in it in the nib and verify that it appears on launch. If so, then you know that at least your view is displaying appropriately. At that point, it would have to be related to #2.
Make sure you connected quote to your UITextField in your XIB. Also, make sure that you #synthesize quote; in your .m.
I just bumped into this too and the problem went away as soon as I specified enough height for the content. In Xcode it may still look all right, but AutoLayout decided to do without the TextView if there was no height-constraint on it.
This was probably not your problem back when you asked the question, but it still turned up fairly early in my google search, therefore I post this answer anyway.
Btw: Xcode is still acting a little skiddish when you edit the constraint. It will update the view (and save) if you hit 'Enter' in the Constant-Field, but it will not do so if it loses focus in some other way.
This just to show us how difficult it is to get user interfaces right all the time.