iOS Getting the value from a TextView in xCode - xcode

I am trying to write a master detail application and I have a TextView and whatever the user enters in the TextView, becomes the new item. But whenever I press the add button it crashes the program with error: object cannot be nil
Here is my code:
#interface MasterViewController () {
NSMuteableArray *_objects;
IBOutlet UITextView *text;
}
And at Insert New Object method I have this:
[_objects insertObject:text.text atIndex:0];
Do I need to hook it up to my actual TextView somehow?
Any help would be amazing, thank you!

Two things:
Make sure you actually wired that outlet up to the TextView
Make sure you allocated that NSMutableArray somewhere
_objects = [[MSMutableArray alloc] init];

Related

How to hide the initial window on start with OS X storyboards

I am creating an OS X status bar application, so I want the application to start hidden.
I have created a "storyboard" application, and the initial window always shows up, even if "Visible at launch" is unchecked (was unchecked by default).
Note: if I disable "Is initial controller" then the app correctly starts without any window, but my (now orphan) window seems to never be added to the storyboard:
var mainWindow = NSStoryboard(name: "Main", bundle: nil)?.instantiateControllerWithIdentifier("mainWindow")
The "mainWindow" controller is not found (even though I correctly set "Storyboard ID" on the Window Controller).
So I think it's better to leave "Is initial controller" but simply have the main window hidden at the start…
Uncheck the "Is Initial Controller" box on the storyboard, leaving your app without an initial controller. Your app will run, but will have no window.
This might be a bit of a hack, but you can do this
func applicationDidFinishLaunching(notification: NSNotification) {
// Insert code here to initialize your application
NSApplication.sharedApplication().windows.last!.close()
}
And then later on...
NSApplication.sharedApplication().windows.last!.makeKeyAndOrderFront(nil)
NSApplication.sharedApplication().activateIgnoringOtherApps(true)
Uncheck "Is Initial Controller", but then you need to set the storyboard and its associated NSWindowController manually.
The precise way of doing that is shown in this answer, which I'll quote here:
[...] in your AppDelegate, set up a property for the window controller:
#property NSWindowController *myController;
In your applicationDidFinishLaunching: method implementation, create a reference to the Storyboard. This way you get access your window controller from the storyboard. After that, the only thing left to do is to display the window by sending your window controller the showWindow: method.
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
#synthesize myController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// get a reference to the storyboard
NSStoryboard *storyBoard = [NSStoryboard storyboardWithName:#"Main" bundle:nil];
// instantiate your window controller
myController = [storyBoard instantiateControllerWithIdentifier:#"secondWindowController"];
// show the window
[myController showWindow:self];
}
#end
The way to do this is just like you tried:
let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let mainWC = storyboard.instantiateControllerWithIdentifier("MainWindowController") as? MainWindowController else {
fatalError("Error getting main window controller")
}
// optionally store the reference here
self.mainWindowController = mainWC
mainWC.window?.makeKeyAndOrderFront(nil) // or use `.showWindow(self)`
The only thing you probably forgot was to uncheck "Release when closed".
This would immediately release the window and prevents the storyboard loading mechanism from finding it even if you had the right identifier.

Change IBOutlet text to property of object returned from custom class

I'm using xCode 4.2.1 - I have an app that uses a web service and fetches some data using a custom class and NSURLConnection. The user taps a "refresh" button which starts a series of events that happen in some custom classes in my project, and I can get the object, along with the properties I want to return in a method in the MainViewController, I'm just not able to change the text of an IBOutlet to a property (NSString) of the returned object.
In my "MainViewController.h, I have an IBOutlet (and it's wired to a button in the MainViewController.xib):
IBOutlet UILabel *textLabel;
and:
#property (nonatomic, retain) IBOutlet UILabel *textLabel;
And I've synthesized the label in MainViewController.m
#synthesize textLabel;
The process looks like this: Refresh Button Tapped --> Fires a mthod in MainViewController --> Fires another method in a custom class (retrieves data from web, creates object) --> Sends Object to MainViewController via: (in my custom class implementation)
// parsing, etc.. and define a string for priceString property of mp object
MainViewController *mvc = [[MainViewController alloc] init];
[mvc log:mp];
And in my MainViewController.m I can access a property of that object and print it in NSLog just fine from this method.
- (void) log:(Price *)mp {
self.textLabel.text = mp.priceString;
NSLog(#"%#", mp.priceString);
}
At this point, I can see the data in the log, but the textLabel text won't change.
I've been trying to read examples for a week, and I've heard everything from delegation, to NSNotication answers, but nothing seems to work.
All I need to do is populate an IBOutlet from a -(void) method.
Any help would be greatly appreciated, and I'm down for implementing delegation, but I'm very new to it, and seeking an example.
EDIT - more details.
After further research, I think I should note that my NSURLConnection that is returning my objects is in a separate class, and I've been reading a lot of threads where people are starting the connection in viewDidLoad.
The problem is that you are calling [mvc log:mp]; just after initializing the view controller. At that stage, the view is still not loaded from the xib file.
you can change log to see that:
- (void) log:(Price *)mp {
if (self.textLabel) {
self.textLabel.text = mp.priceString;
} else {
NSLog(#"textLabel is nil.");
}
}
You can call [mvc log:mp]; in the view controller viewDidLoad method, or after viewDidLoad has been executed.
To understand this better, add NSLog(#"viewDidLoad."); in viewDidLoad method, and add NSLog(#"viewController initialized."); just after the code where you initialized the view controller.
You will see the following
viewController initialized.
textLabel is nil. // if you call log as usual after initializing the view controller.
viewDidLoad.

Show NSPopover from NSToolbarItem Button

I want to show an NSPopover from an NSToolbarItem button in my toolbar.
(i.e. positioned below the button).
Ideally, I want to pass the NSView of the button to the popover to position it.
My question is, how do I get the NSView of the NSToolbarItem?
[toolbarbutton view] always returns nil.
The answer appears to be in the video for the 2011 WWDC Session 113, "Full Screen and Aqua Changes." Basically, put an NSButton inside the NSToolbaritem and use the view of that.
A blog post is here: http://www.yellowfield.co.uk/blog/?p=33, and a sample project is on github at http://github.com/tevendale/ToolbarPopover
All in the sprit of http://xkcd.com/979!
You can send the action directly from the NSButton enclosed in the NSToolbarItem (which is what you should generally do anyways, consider segmented controls, where each segment has its own target/action), and that will do the trick.
Instead of getting the view from the IBAction sender, connect an IBOutlet directly to the toolbar item and use that to get the relative view:
In your header file:
#property (weak) IBOutlet NSToolbarItem *theToolbarItem;
#property (weak) IBOutlet NSPopover *thePopover;
In your implementation file, to show the popover:
[self.thePopover showRelativeToRect:[[self.theToolbarItem view] bounds] ofView:[self.theToolbarItem view] preferredEdge:NSMinYEdge];
This will also work for showing popups from menu item selections inside a toolbar item.
While I did achieve that the Popover was shown using the approach mentioned by Stuart Tevendale, I did run into problems when I tried to validate (enable / disable) the NSToolbarItems using the NSToolbarDelegate:
-(BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
BOOL enable = YES;
NSString *identifier = [toolbarItem itemIdentifier];
// This does never get called because I am using a button inside a custom `NSToolbarItem`
if ([identifier isEqualToString:#"Popover"]) {
return [self someValidationMechanism];
}
// For this the validation works when I am using a standard `NSToolbarItem`
else if ([identifier isEqualToString:#"StandardToolbarItem"]){
return [self someOtherValidationMechanism];
}
return enable;
}
So I would advise not to display a Popover from NSToolbarItem. An alternative might be to show a Page Sheet: How to show a NSPanel as a sheet

Disable/Enable NSButton if NSTextfield is empty or not

I´m newbie with cocoa. I have a button and a textField in my app. I want the button disabled when the textfield is empty and enabled when the user type something.
Any point to start? Any "magic" binding in Interface Builder?
Thanks
[EDITED]
I´ve tried to set the appDelegate as the NSTextfield´s delegate and added this method (myTextfield and myButton are IBOutlets):
- (void)textDidChange:(NSNotification *)aNotification
{
if ([[myTextField stringValue]length]>0) {
[myButton setEnabled: YES];
}
else {
[myButton setEnabled: NO];
}
}
But nothing happens...
I´ve tried to set the appDelegate as the NSTextfield´s delegate and added this method (myTextfield and myButton are IBOutlets):
- (void)textDidChange:(NSNotification *)aNotification
{
if ([[myTextField stringValue]length]>0) {
[myButton setEnabled: YES];
}
else {
[myButton setEnabled: NO];
}
}
That's the hard way, but it should work just fine. Either you haven't hooked up the text field's delegate outlet to this object, you haven't hooked up the myTextField outlet to the text field, or you haven't hooked up the myButton outlet to the button.
The other way would be to give the controller a property exposing the string value, bind the text field's value binding to this stringValue property, and bind the button's enabled binding to the controller's stringValue.length.
You could also give the controller two properties, one having a Boolean value, and set that one up as dependent upon the string property, and bind the button to that. That's a cleaner and possibly more robust solution, though it is more work.
Here's a solution using bindings.
Below I setup a NSTextField that is bound to the file owner's "text" property. "text" is a NSString. I was caught by "Continuously Updates Value". Thinking my solution didn't work but really it wasn't updating as the user typed, and only when the textfield lost focus.
And now setting up bindings on the button, simply set its enabled state to the length of the file owner's text property.
Annd, the working product.
If you use controlTextDidChange instead of textDidChange, you can get rid of the notification stuff and just rely on being the NSTextField's delegate.
Thanks Peter. What I missed (in my hard way version) is this piece of code in the awakeFromNib in the appDelegate:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(textDidChange:) name:NSControlTextDidChangeNotification object:myTextField];
It works perfect. Now I´m trying the easy way, but I´m afraid I´m not still good enough with the bindings.
To bind the property
#property (retain) IBOutlet NSString *aStringValue;
with the textfield´s value, what I have to use in IB for "Bind to:", "Controller Key" and "Model Key Path"?

Calling function from popover

Alright, so I made a popover from my main view and all that good stuff. But I want to have my popover call an action in my main view when a button within the popover is pressed.
MainView *mainView = [[MainView alloc] initWithNibName:#"MainView" bundle:nil];
[mainView doStuff];
The "dostuff" function changes some elements within the view. For example, the color of the toolbar is supposed to be changed. I've put a print command and the print command executes. But for some reason, the toolbar won't change color.
I've imported the header of MainView into the popover.
I did an #class thingy for MainView in my popover.
doStuff is declared in MainView's header.
The IBOutlets are declared too, and connected.
Any ideas?
Well its disappointing that we have no direct method that can be used to check in which view (view controller) the popover is shown. The thing that I am doing in tabbased application is:
New_iPadAppDelegate *appDel = (New_iPadAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *viewConts = [(UINavigationController *)[[appDel tabBarController] selectedViewController] viewControllers];
MainViewController *viewController = (MainViewController *)[viewConts lastObject];
if([[viewController popoverController] isPopoverVisible]){
[viewController doStuff];
}
Hope this helps,
I know this is not the best way, hoping apple thinks about this issue, or if somebody has devised a work around.
Thanks,
Madhup

Resources