Creating an OSX application without Xcode - xcode

I'm very new to OS X, and I'm trying to create a simple application without Xcode. I did found some other sites doing that, but I cannot attach event handlers to my button.
below is the code (crafted from other sites). It creates a window and a button, but I don't know how to attach that event to the button:
#import <Cocoa/Cocoa.h>
#interface myclass
-(void)buttonPressed;
#end
#implementation myclass
-(void)buttonPressed {
NSLog(#"Button pressed!");
//Do what You want here...
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:#"Hi there."];
[alert runModal];
}
#end
int main ()
{
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
id appMenu = [[NSMenu new] autorelease];
id appName = [[NSProcessInfo processInfo] processName];
id quitTitle = [#"Quit " stringByAppendingString:appName];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
action:#selector(terminate:) keyEquivalent:#"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
autorelease];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle:appName];
[window makeKeyAndOrderFront:nil];
int x = 10;
int y = 100;
int width = 130;
int height = 40;
NSButton *myButton = [[[NSButton alloc] initWithFrame:NSMakeRect(x, y, width, height)] autorelease];
[[window contentView] addSubview: myButton];
[myButton setTitle: #"Button title!"];
[myButton setButtonType:NSMomentaryLightButton]; //Set what type button You want
[myButton setBezelStyle:NSRoundedBezelStyle]; //Set what style You want
[myButton setAction:#selector(buttonPressed)];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}

First of all, don't avoid Xcode because you are a beginner. Being a beginner is one of the many reasons to use Xcode. Resorting to fully-manually-implemented code like what you have is a naive way to develop applications for OS X and you'll only encounter far more difficulties than it is worth, especially for anything non-trivial.
Having said that, the reason your button isn't doing anything is because the button doesn't have a target. All actions require a target. In your case, you want to create an instance of your myclass class (note that class names in Objective-C are conventionally named in upper camelcase, i.e. MyClass). Note that also your action method should take an argument (which is the sender of the action), even if it is unused.
- (void) buttonPressed:(id) sender
{
NSLog(#"Button pressed!");
//Do what You want here...
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:#"Hi there."];
[alert runModal];
}
// ...
myclass *mc = [[myclass alloc] init];
[myButton setTarget:mc];
[myButton setAction:#selector(buttonPressed:)];
I can't stress enough how ridiculous all of this code is. Bite the bullet and dive into Xcode!

Related

UISplitViewControllerDisplayModePrimaryOverlay causes "Unbalanced calls to begin/end appearance transitions"

In iOS 8, setting the preferredDisplayMode on a UISplitViewController to PrimaryOverlay is generating the following warning:
"Unbalanced calls to begin/end appearance transitions for UINavigationController"
There is no problem if I set preferredDisplayMode to AllVisible or don't set it at all. Problem occurs for all iPads and iPhones in the simulator that I've tried. Problem occurs whether the app starts up in portrait or landscape.
Here's some very simple code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UITableViewController *tableViewController = [[UITableViewController alloc] init];
UIViewController *viewController = [[UIViewController alloc] init];
UINavigationController *masterNavController = [[UINavigationController alloc] initWithRootViewController:tableViewController];
UINavigationController *detailNavController = [[UINavigationController alloc] initWithRootViewController:viewController];
UISplitViewController *svc = [[UISplitViewController alloc] init];
[svc addChildViewController:masterNavController];
[svc addChildViewController:detailNavController];
//svc.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
svc.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;
self.window.rootViewController = svc;
[self.window makeKeyAndVisible];
return YES;
}
Wrap your display code in dispatch_async. Otherwise iOS seems to get confused with other animations running at the same time.
dispatch_async(dispatch_get_main_queue(), ^{
svc.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;
});
or
dispatch_async(dispatch_get_main_queue()) {
svc.preferredDisplayMode = .PrimaryOverlay
}

Correct way to get rearrangeObjects sent to an NSTreeController after changes to nodes in the tree?

What is the appropriate way to get rearrangeObjects to be sent to an NSTreeController after changes to nodes in the tree? I have a sample application (full code below) using an NSOutlineView and NSTreeController with a simple tree of Node objects.
In Version1 of the app, when you edit the name of a Node, the tree doesn't get resorted until you click the column header or use the “Rearrange” item in the menu. The latter is set up to directly send rearrangeObjects to the NSTreeController.
In Version2, I tried sending rearrangeObjects from the Node's setName: method. This doesn't seem like a good solution because it means the model now has knowledge of the view/controller. Also, it has the side effect that the outline view loses focus after the rename (if you select a Node and edit its name, the selection bar turns from blue to gray) for some reason (why is that?).
NSArrayController has a method setAutomaticallyRearrangesObjects: but NSTreeController does not? So what is the appropriate way to solve this?
/* example.m
Compile version 1:
gcc -framework Cocoa -o Version1 example.m
Compile version 2:
gcc -framework Cocoa -o Version2 -D REARRANGE_FROM_SETNAME example.m
*/
#import <Cocoa/Cocoa.h>
NSTreeController *treeController;
NSOutlineView *outlineView;
NSScrollView *scrollView;
#interface Node : NSObject {
NSString *name;
NSArray *children;
}
#end
#implementation Node
- (id) initWithName: (NSString*) theName children: (id) theChildren
{
if (self = [super init]) {
name = [theName retain];
children = [theChildren retain];
}
return self;
}
- (void) setName: (NSString*) new
{
[name autorelease];
name = [new retain];
#ifdef REARRANGE_FROM_SETNAME
[treeController rearrangeObjects];
#endif
}
#end
NSArray *createSortDescriptors()
{
return [NSArray arrayWithObject: [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
}
void createTheTreeController()
{
Node *childNode1 = [[[Node alloc] initWithName:#"B" children:[NSArray array]] autorelease];
Node *childNode2 = [[[Node alloc] initWithName:#"C" children:[NSArray array]] autorelease];
Node *childNode3 = [[[Node alloc] initWithName:#"D" children:[NSArray array]] autorelease];
Node *topNode1 = [[[Node alloc] initWithName:#"A" children:[NSArray arrayWithObjects:childNode1,childNode2,childNode3,nil]] autorelease];
Node *topNode2 = [[[Node alloc] initWithName:#"E" children:[NSArray array]] autorelease];
Node *topNode3 = [[[Node alloc] initWithName:#"F" children:[NSArray array]] autorelease];
NSArray *topNodes = [NSArray arrayWithObjects:topNode1,topNode2,topNode3,nil];
treeController = [[[NSTreeController alloc] initWithContent:topNodes] autorelease];
[treeController setAvoidsEmptySelection:NO];
[treeController setChildrenKeyPath:#"children"];
[treeController setSortDescriptors:createSortDescriptors()];
}
void createTheOutlineView()
{
outlineView = [[[NSOutlineView alloc] initWithFrame:NSMakeRect(0, 0, 284, 200)] autorelease];
[outlineView bind:#"content" toObject:treeController withKeyPath:#"arrangedObjects" options:nil];
[outlineView bind:#"sortDescriptors" toObject:treeController withKeyPath:#"sortDescriptors" options:nil];
[outlineView bind:#"selectionIndexPaths" toObject:treeController withKeyPath:#"selectionIndexPaths" options:nil];
NSTableColumn *column = [[[NSTableColumn alloc] initWithIdentifier:#"NameColumn"] autorelease];
[[column headerCell] setStringValue:#"Name"];
[outlineView addTableColumn:column];
[outlineView setOutlineTableColumn:column];
[column bind:#"value" toObject:treeController withKeyPath:#"arrangedObjects.name" options:nil];
[column setWidth:250];
scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 300, 200)] autorelease];
[scrollView setDocumentView:outlineView];
[scrollView setHasVerticalScroller:YES];
}
void createTheWindow()
{
id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 300, 200)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
autorelease];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle:#"Window"];
[window makeKeyAndOrderFront:nil];
[[window contentView] addSubview:scrollView];
}
void createTheMenuBar()
{
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
id appMenu = [[NSMenu new] autorelease];
#ifndef REARRANGE_FROM_SETNAME
id rearrangeMenuItem = [[[NSMenuItem alloc] initWithTitle:#"Rearrange"
action:#selector(rearrangeObjects) keyEquivalent:#"r"] autorelease];
[rearrangeMenuItem setTarget: treeController];
[appMenu addItem:rearrangeMenuItem];
#endif
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:#"Quit"
action:#selector(terminate:) keyEquivalent:#"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
}
void setUpAutoReleasePoolAndApplication()
{
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}
void activateAppAndRun()
{
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
}
int main(int argc, const char * argv[])
{
setUpAutoReleasePoolAndApplication();
createTheTreeController();
createTheOutlineView();
createTheWindow();
createTheMenuBar();
activateAppAndRun();
return 0;
}
I'm at least able to partly answer my own question after having looked at Apple's iSpend sample application. Their file TransactionsController_Sorting.m includes a method scheduleRearrangeObjects that invokes rearrangeObjects in a different way. Changing my own code in the same way means including this snippet in the setName: method:
#ifdef REARRANGE_FROM_SETNAME
// Commented out: [treeController rearrangeObjects];
[treeController performSelector:#selector(rearrangeObjects) withObject:nil afterDelay:0.0];
#endif
With this change, the outline view no longer loses focus after renaming a node. What's left to do now is take this code out of the model and into the view/controller; TransactionsController_Sorting seems to also illustrate how to do that. (I still don't understand why the above change prevents the outline view from losing focus though, anyone have an explanation?)
Another answer, as a possible explanation
I believe rearrangeObjects and fetch are delayed until the next runloop iteration. fetch at least tells you so in the docs:
Special Considerations
Beginning with OS X v10.4 the result of this method is deferred until the next iteration of the runloop so that the error presentation mechanism can provide feedback as a sheet.
In my own experimentation, I can use dispatch_async after rearrangeObjects to get code executed after the rearrange. In other words, if I don't dispatch_async code following rearrangeObjects it will be applied before the deferred rearrange. It's a great way to tear out your hair.
In any event, I suspect you were losing focus because rearrangeObjects blows away the context in which you were editing a node as it reloads the whole object tree, but if you force it to execute immediately, you don't lose that context.
[edit] Update here. I was dealing with rearrangeObjects and core data not seeming synchronous, and sure enough, it isn't. I caught an arrayController calling dispatch_async through a binding stack trace.

UIPickerController - want to show specific selection upon loading

I have a custom UIPickerView that is embedded into an UIActionsheet that comes up half the screen when called. Works great. The problem is that I want to have the barrelPicker be showing the 'most probable' results as the selection when it first comes up (after all the data has been loaded into it).
Prior to having the custom picker embedded in the action sheet, I had it in a UIViewController and was just calling "showProbableResults" (a custom method of mine) in the viewDidLoad method of the ViewController, because I knew at that point, the UIPickerView would be loaded up and ready to go. Is there an equivalent place for me to call this method now or do I need to rethink my whole design? Essentially, what I need is a place to call this after the UIPickerView has been loaded.
- (void)startWithDelegate:(UIViewController <ImageProcessingDelegate> *)sender
{
self.delegate = sender;
self.showFirstBottle = YES;
[self start];
}
- (void) start {
self.actionSheet = [[UIActionSheet alloc] initWithTitle:#"Choose Something"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
[self.actionSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
CGRect pickerFrame = CGRectMake(0, 40, 0, 0);
NSLog(#"1.) About to alloc the picker");
self.vintagePicker = [[UIPickerView alloc] initWithFrame:pickerFrame];
self.vintagePicker.showsSelectionIndicator = YES;
self.vintagePicker.dataSource = self;
self.vintagePicker.delegate = self;
[self.actionSheet addSubview:self.vintagePicker];
[self.vintagePicker release];
UISegmentedControl *nextButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:#"Next"]];
nextButton.momentary = YES;
nextButton.frame = CGRectMake(260, 7.0f, 50.0f, 30.0f);
nextButton.segmentedControlStyle = UISegmentedControlStyleBar;
nextButton.tintColor = [UIColor blackColor];
[nextButton addTarget:self action:#selector(show:) forControlEvents:UIControlEventValueChanged];
[self.actionSheet addSubview:nextButton];
[nextButton release];
UISegmentedControl *backButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:#"Back"]];
backButton.momentary = YES;
backButton.frame = CGRectMake(10, 7.0f, 50.0f, 30.0f);
backButton.segmentedControlStyle = UISegmentedControlStyleBar;
backButton.tintColor = [UIColor blackColor];
[backButton addTarget:self action:#selector(cancel:) forControlEvents:UIControlEventValueChanged];
[self.actionSheet addSubview:backButton];
[backButton release];
[self.actionSheet showInView:_delegate.parentViewController.tabBarController.view]; // show from our table view (pops up in the middle of the table)
[self.actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
}
One option is to create a reusable picker view, as shown here, and then call the #optional
-(void)setInitialPickerValueToRow:(int)i inComponent:(int)j animated:(BOOL)k{
[pickerView selectRow:i inComponent:j animated:k];
}
from any view controller or NSObject (such as your UIActionSheetDelegate).
Your other option is to set a standard pre-chosen selection, as follows:
self.vintagePicker = [[UIPickerView alloc] initWithFrame:pickerFrame];
self.vintagePicker.showsSelectionIndicator = YES;
self.vintagePicker.dataSource = self;
self.vintagePicker.delegate = self;
[self.actionSheet addSubview:self.vintagePicker];
[self.vintagePicker selectRow:0 inComponent:0 animated:NO];
You could also have different variables that you set, and have selectRow:inComponent:animated: access those, instead of being preset.
[self.vintagePicker selectRow:myRow inComponent:0 animated:NO];
Hope this helps!

InputAccessoryView of a UITextField

I need to stick a toolBar with a UITextField inside to a keyboard.
What if I make the whole toolBar (which I think is the textField's super view) the inputAccessoryView of its textField?
I mean like this:
textField.inputAccessoryView = toolBar; // textField is inside the toolBar
I've been trying on this but I have not made it work yet.
Anyone can help?
Or is there another way to achieve what I want?
- (void)viewDidLoad
{
[self createAccessoryView];
[textField setDelegate:self];
[textField setKeyboardType:UIKeyboardTypeDefault];
[textField setInputAccessoryView:fieldAccessoryView];
}
- (void)createAccessoryView
{
CGRect frame = CGRectMake(0.0, self.view.bounds.size.height, self.view.bounds.size.width, 44.0);
fieldAccessoryView = [[UIToolbar alloc] initWithFrame:frame];
fieldAccessoryView.barStyle = UIBarStyleBlackOpaque;
fieldAccessoryView.tag = 200;
[fieldAccessoryView setBarStyle:UIBarStyleBlack];
UIBarButtonItem *spaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done:)];
UISegmentedControl* segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:NSLocalizedString(#"Previous", #""), NSLocalizedString(#"Next", #""), nil]];
[segmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
[segmentedControl setMomentary:YES];
UIBarButtonItem *segmentButton = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[fieldAccessoryView setItems:[NSArray arrayWithObjects:segmentButton, spaceButton, doneButton, nil] animated:NO];
[segmentButton release];
[spaceButton release];
[doneButton release];
[segmentedControl release];
}
When are you setting the property? You may be setting it after the view has already loaded.
Try setting it in
- (void) viewDidLoad

Init a UInavigationbar

I am having trouble in making a tableview with navigation bar or controller on top.
I have this piece of code,
- (void)viewDidLoad {
[super viewDidLoad];
UINavigationController *addNavCon = [[UINavigationController alloc]initWithNibName:#"Welcome" bundle:nil];
self.navigationItem.rightBarButtonItem = self.addButtonItem;
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self createEditableCopyOfDatabaseIfNeeded];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
NSString *documentDirectory = [self applicationDocumentsDirectory];
NSString *path = [documentDirectory stringByAppendingPathComponent:#"notebook.plist"];
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
self.Notes = tmpArray;
[tmpArray release];
}
However, the navigation bar never show up, while the table is doing fine. May i know what's the problem with the code?
Many thanks
You have created an instance of UINavigationController but never added it to anything. You need to add it to the current hierarchy or it will not appear.
If you wish to add the navController to the entire app, then you should do this in the App Delegate by
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
UIViewController *rootController = [[MyRootViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:rootController];
[rootController release];
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
}

Resources