I have been trying to create an app for Cocoa without a nib/xib (No, I don't want to use a nib/xib. I want to be in full control programatically) and I can't seem to be able to catch the Events such as keystrokes and mouse clicks. Here is the code I have so far:
Main.m
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[app setDelegate:appDelegate];
[app activateIgnoringOtherApps:YES];
[app run];
}
return EXIT_SUCCESS;
}
AppDelegate.h/m
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow *window;
}
#end
#import "AppDelegate.h"
#import "GLView.h"
#implementation AppDelegate
- (id)init{
self = [super init];
if (!self) {
return nil;
}
NSRect bounds = [[NSScreen mainScreen] frame];
GLView *view = [[GLView alloc]initWithFrame:bounds];
window = [[NSWindow alloc] initWithContentRect:bounds
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[window setReleasedWhenClosed:YES];
[window setAcceptsMouseMovedEvents:YES];
[window setContentView:view];
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[window makeKeyAndOrderFront:self];
}
#end
GLView.h/m
#import <Cocoa/Cocoa.h>
#interface GLView : NSView
#end
#import "GLView.h"
#implementation GLView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
}
- (BOOL)canBecomeKeyView
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (BOOL)becomeFirstResponder
{
return YES;
}
- (BOOL)resignFirstResponder
{
return YES;
}
- (void)keyDown:(NSEvent *)theEvent
{
NSString* const character = [theEvent charactersIgnoringModifiers];
unichar const code = [character characterAtIndex:0];
NSLog(#"Key Down: %hu", code);
switch (code)
{
case 27:
{
EXIT_SUCCESS;
break;
}
}
}
- (void)keyUp:(NSEvent *)theEvent
{
}
#end
Nothing I have tried for it has worked. I thought that by setting the view as the first responder I would be able to get the events. So far... Not working. Any ideas on how I can fix this? Remember, NO NIB.
Thanks,
Tyler
First, you need to make sure that your window can actually become key, by subclassing and returning YES from canBecomeKeyWindow, because windows without title bars cannot become key by default.
Next, your build target needs to be an application. I would guess that you're starting from the Command-Line Tool template in Xcode. This is fine, but you need to produce an application bundle in order for your app to recieve key events. Create a new target in your project that will build a Cocoa Application. It needs to have an Info.plist file (from which you'll want to delete the "Main nib file base class" entry) and have a "Copy Bundle Resources" Build Phase.
I can't quite figure out what all the other differences are in the build process, but starting from your code, I got the window to accept key events with these two steps.
Related
I'm new at objective-c/IOS and i'm following this tutorial about implementing user's current location on a map. Followed the instructions, but when I press the button to see my current location, nothing happens. Can anybody give me a hint? I'm using the latest version of Xcode and i'm not sure if the one in the tutorial is the same...The code:
ViewController.m:
#import "ViewController.h"
#import "Pin.h"
#interface ViewController ()
#end
#implementation ViewController;
#synthesize mapview;
- (void)viewDidLoad {
[super viewDidLoad];
MKCoordinateRegion region={{0.0,0.0},{0.0,0.0}};
region.center.latitude=38.711995;
region.center.longitude=-9.144932;
region.span.longitudeDelta=0.01f;
region.span.latitudeDelta=0.01f;
[mapview setRegion:region animated:YES];
Pin*vega =[[Pin alloc] init];
vega.title=#"Capela";
vega.subtitle=#"Bar";
vega.coordinate= region.center;
[mapview addAnnotation:vega];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)SetMap:(id)sender{
switch (((UISegmentedControl *) sender).selectedSegmentIndex) {
case 0:
mapview.mapType=MKMapTypeStandard;
break;
case 1:
mapview.mapType=MKMapTypeSatellite;
break;
case 2:
mapview.mapType=MKMapTypeHybrid;
break;
default:
break;
}
}
-(IBAction)GetLocation:(id)sender{
mapview.showsUserLocation=YES;
}
(IBAction)Directions:(id)sender{
NSString * urlString=#"http://maps.apple.com/maps?daddr=38.711995, -9.144932";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
}
#end
VieController.h:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface ViewController : UIViewController{
MKMapView * mapview;
}
#property(nonatomic,retain)IBOutlet MKMapView* mapview;
-(IBAction)SetMap:(id)sender;
-(IBAction)GetLocation:(id)sender;
-(IBAction)Directions:(id)sender;
#end
And in the Connections Inspector everything (buttons) is set up..
You have to check/get the location permission :
add it to your plist file
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
and add that code to your VC:
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)])
{
[self.locationManager requestWhenInUseAuthorization];
}
I have created a main dialog (MainMenu.xib) and created two button on mainmenu.xib file and when click on each button, it will launch different xib file(say for button1 xib file is button1.xib and for button2 xib file is button2.xib file).
Now, My question I need the value of button1.xib file into button2.xib file.
I have tried the code
id appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate] ;
this code always gives the delegate of active dialog.
Can you please tell me how to get the control or object of inactive dialog?
Thanks,
What you need is model object. Below is really simple approach how you should start. This model object should be stored in AppDelegate for non-document based app. Second window should not know about existence of any other window. The same applies for first window. It shouldn't know more that displaying/working with model.
Accessing model through delegate in fact accessing delegate like below is considered as really, really, really bad.
id model = [(AppDelegate*)[[NSApplication sharedApplication] delegate] model];
Simple example how you can implement model and monitor for changes
#import "AppDelegate.h"
#import "FirstWC.h"
#import "SecondWC.h"
#import "Model.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (nonatomic, strong) FirstWC *firstWC;
#property (nonatomic, strong) SecondWC *secondWC;
#property (nonatomic, strong) Model *model;
#end
#implementation AppDelegate
- (instancetype)init
{
self = [super init];
if (self) {
_model = [[Model alloc] init];
}
return self;
}
- (IBAction)changeColor:(id)sender
{
NSUInteger number = arc4random() % 4;
switch (number) {
case 0:
self.model.color = [NSColor redColor];
break;
case 1:
self.model.color = [NSColor blueColor];
break;
case 2:
self.model.color = [NSColor purpleColor];
break;
case 3:
self.model.color = [NSColor yellowColor];
break;
default:
break;
}
}
- (IBAction)showFirstWC:(id)sender
{
if (!_firstWC) {
_firstWC = [[FirstWC alloc] initWithModel:self.model];
}
[[_firstWC window] makeKeyAndOrderFront:self];
}
- (IBAction)showSecondWC:(id)sender
{
if (!_secondWC) {
_secondWC = [[SecondWC alloc] initWithModel:self.model];
}
[[_secondWC window] makeKeyAndOrderFront:self];
}
#end
Model:
#import <Cocoa/Cocoa.h>
#interface Model : NSObject
#property (nonatomic, strong) NSColor *color;
#end
#import "Model.h"
#implementation Model
- (instancetype)init
{
self = [super init];
if (self) {
_color = [NSColor blueColor];
}
return self;
}
#end
Window Controller:
#import "FirstWC.h"
#import "Model.h"
#interface FirstWC ()
#property (nonatomic, strong) Model *model;
#end
#implementation FirstWC
- (instancetype)initWithModel:(Model *)model
{
self = [super initWithWindowNibName:#"FirstWC"];
if (self) {
_model = model;
[self addObserver:self
forKeyPath:#"model.color"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
- (void)windowDidLoad {
[super windowDidLoad];
self.window.backgroundColor = [self model].color;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:#"model.color"]) {
self.window.backgroundColor = self.model.color;
}
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:#"model.color"];
}
#end
#import "SecondWC.h"
#interface SecondWC ()
#property (nonatomic, strong) Model *model;
#end
#implementation SecondWC
- (instancetype)initWithModel:(Model *)model
{
self = [super initWithWindowNibName:#"SecondWC"];
if (self) {
_model = model;
[self addObserver:self
forKeyPath:#"model.color"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
- (void)windowDidLoad {
[super windowDidLoad];
self.window.backgroundColor = [self model].color;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:#"model.color"]) {
self.window.backgroundColor = self.model.color;
}
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:#"model.color"];
}
#end
Add properties for both button1 and button2 in your controller, and a (weak) property for button2 in the button1 controller, and vice versa.
- (void)createButtons {
DlgController1 = [[Button1 alloc] initWithWindowNibName:#"Button1"];
DlgController2 = [[Button2 alloc] initWithWindowNibName:#"Button2"];
DlgController1.button2 = DlgController2;
DlgController2.button1 = DlgController1;
}
- (IBAction)Button1:(id)sender {
[DlgController1 showWindow:self];
[NSApp runModalForWindow:DlgController1.window];
[NSApp endSheet:DlgController1.window];
[DlgController1.window orderOut:self];
[DlgController1 close];
}
I have tried the code
id appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate] ;
this code always gives the delegate of active dialog.
I'm not quite sure how this relates to the rest of your question, but the above issue suggests that you have an instance of your AppDelegate class in button1.xib and button2.xib. You should not.
There is only one application object in your app. It should have only one delegate. That instance is created in the MainMenu NIB and connected to the application's delegate outlet using the File's Owner placeholder's outlet. That should be the only instance of AppDelegate in your whole app.
If you also create an instance of AppDelegate in the other NIBs, then there will be multiple instances of that class once those NIBs are loaded, which will just confuse things. Also, if those instances are also connected to the application's delegate outlet, then they will replace the original delegate. That explains the symptom you described above where [[NSApplication sharedApplication] delegate] gives an object from the last NIB to load.
I'm not sure what button1.xib and button2.xib are supposed to contain. I'm guessing they are supposed to contain a window. In that case, you should write a custom subclass of NSWindowController to be the window controller for each window NIB. You would instantiate that class in the button's action method and provide it with whatever information it needs (possibly including a reference to the app delegate). That controller class would be responsible for loading the NIB. It would serve as the NIB's owner and would be represented in the NIB by the File's Owner placeholder. It's pretty common to make the window controller be the window's delegate, too. I recommend that you follow the advice of this blog post.
I currently developing a small dictionary app under OSX for my own use, I would like to have a feature that when I hit the return key, the focus would go to the nssearchfeild.
So I try to make the app to receive keyDown event using a NSView and NSViewController told by this tutorial.
But every time I start the app, it wouldn't receive the keyDown event. I have to click on the window once, then hit the keyboard, so that it can receive keyDown event.
What did I do wrong? Can anyone help me out with this problem? I have been stuck in this problem for days, and searching throught Google and API wouldn't help much.
Thanks in advance!
Here is my code for AppDelegate.m
#import "AppDelegate.h"
#import "MyDictViewController.h"
#interface AppDelegate()
#property (nonatomic,strong) IBOutlet MyDictViewController *viewController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.viewController = [[MyDictViewController alloc] initWithNibName:#"MyDictViewController" bundle:nil];
[self.window.contentView addSubview:self.viewController.view];
self.viewController.view.frame = ((NSView*)self.window.contentView).bounds;
[self.window makeKeyAndOrderFront:nil];
}
#end
And My ViewController.m
#import "MyDictViewController.h"
#import "FileHelper.h"
#import <Carbon/Carbon.h>
#interface MyDictViewController ()
#property (weak) IBOutlet NSTableView *wordsFilteredTable;
#end
#implementation MyDictViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.IMAGE_FILE = [NSImage imageNamed:#"Document.png"];
self.wordlist = [FileHelper readLines];
self.filterWordlist = [[NSMutableArray alloc] init];
}
return self;
}
- (void)loadView
{
[super loadView];
[self.view becomeFirstResponder];
}
-(void)keyDown:(NSEvent*)theEvent
{
NSLog(#"Caught key event");
}
-(void)keyUp:(NSEvent *)theEvent
{
unsigned short keycode = [theEvent keyCode];
switch (keycode)
{
case kVK_Return:
[self.searchField becomeFirstResponder];
default:
break;
}
}
-(void)mouseDown:(NSEvent*)theEvent
{
NSLog(#"Caught mouse event");
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
self.wordsFilteredTable.rowHeight = 37;
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if( [tableColumn.identifier isEqualToString:#"WordColumn"] )
{
NSString *word = [self.filterWordlist objectAtIndex:row];
cellView.textField.stringValue = word;
cellView.imageView.image = self.IMAGE_FILE;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.filterWordlist count];
}
- (void)controlTextDidChange:(NSNotification *)obj
{
NSTextView* textView = [[obj userInfo] objectForKey:#"NSFieldEditor"];
self.currentWord = [textView string];
[self.filterWordlist removeAllObjects];
for(NSString* word in self.wordlist) {
if ([word hasPrefix:self.currentWord]) {
[self.filterWordlist addObject:word];
}
}
[self.wordsFilteredTable reloadData];
}
#end
And my AppView.m
#import "AppView.h"
#implementation AppView
- (void)setViewController:(NSViewController *)newController
{
if (viewController)
{
[super setNextResponder:[viewController nextResponder]];
[viewController setNextResponder:nil];
}
viewController = newController;
if (newController)
{
[super setNextResponder: viewController];
[viewController setNextResponder:[self nextResponder]];
}
}
- (void)setNextResponder:(NSResponder *)newNextResponder
{
if (viewController)
{
[viewController setNextResponder:newNextResponder];
return;
}
[super setNextResponder:newNextResponder];
}
#end
I am doing the parallax effect by using category by doing :
add and UIView into the uitableView (via category
add addObserver:forKeyPath so that whenever tableview is moving, i will reframe the view above
Details are below
UIScrollView+Parallax.h
#import <UIKit/UIKit.h>
#class ParallaxView;
#interface UIScrollView (Parallax)
#property (strong, nonatomic) ParallaxView *parallaxView;
- (void) addParallaxViewWith:(UIView*)parallaxView;
- (void) removeKVO;
#end
#interface ParallaxView : UIView
#end
UIScrollView+Parallax.m
static char parallaxKey;
#implementation UIScrollView (Parallax)
#dynamic parallaxView;
#pragma mark - Add parallax view to scrollView
- (void) addParallaxViewWith:(ParallaxView*)pView {
if ( !self.parallaxView) {
[self addSubview:pView];
[self setParallaxView:pView];
}
}
#pragma mark - Set parallaxView + register parallaxView as an observer
- (void) setParallaxView:(ParallaxView *)parallaxView {
objc_setAssociatedObject(self, ¶llaxKey, parallaxView, OBJC_ASSOCIATION_ASSIGN);
/* THESE LINE ARE CRASHING THE APP */
// [self addObserver:self.parallaxView
// forKeyPath:#"contentOffset"
// options:NSKeyValueObservingOptionNew
// context:nil];
}
#pragma mark - Get parallaxView
- (ParallaxView*) parallaxView {
return (objc_getAssociatedObject(self, ¶llaxKey));
}
#pragma mark - Remove
- (void)removeKVO {
[self removeObserver:self.parallaxView forKeyPath:#"contentOffset"];
}
#end
#implementation ParallaxView
-(id)init
{
//load xib from main bundle and assign it to self
self = [[[NSBundle mainBundle]loadNibNamed:#"Parallex"
owner:self
options:nil] objectAtIndex:0];
return self;
}
-(id)initWithFrame:(CGRect)frame
{
self = [self init];
[self setFrame:frame];
return self;
}
................
#end
And I am adding parallax to the table by doing
ParallaxView *pView = [[ParallaxView alloc]initWithFrame:CGRectMake(0, 0, 320, 160)];
[self.tableView addParallaxViewWith:pView];
However, [self addObserver:forKeyPath:options:context:nil] keeps crashing the app without no clues at all. If I comments this line out and app is not crashing but parallex effect is not working.
Any ideas for this problematics. Please help. Thanks
Problem in code
-(id)initWithFrame:(CGRect)frame
{
self = [self init];
[self setFrame:frame];
return self;
}
In above code self = [self init]; and [self setFrame:frame]; will go in recursion will give crash
,First fix this I guess it will solve your problem,It should be like this
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
and also loading View from nib using
self = [[[NSBundle mainBundle]loadNibNamed:#"Parallex"
owner:self
options:nil] objectAtIndex:0];
this code is really a bad idea.
you can refer THIS for this task.
Happy and clean coding...
#implementation ParallaxView
//Add Observe Method
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if([keyPath isEqualToString:#"contentOffset"]){
NSLog(#"contentOffset:%#", [change objectForKey:NSKeyValueChangeNewKey]);
}
}
#end
Try to replace
objc_setAssociatedObject(self, ¶llaxKey, parallaxView, OBJC_ASSOCIATION_ASSIGN);
with
//Change to OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, ¶llaxKey, parallaxView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
parallaxView should be a strong reference.
I have an NSDocument subclass with two NSWindowControllers corresponding to 2 different xib.
Following the Document-Based Application Guide I have added the following in my document.m implementation
- (void)makeWindowControllers
{
NSLog(#"in MakeWindowControllers");
MainWindowController *mainWindowController = [[MainWindowController alloc] init];
[mainWindowController autorelease];
[self addWindowController:mainWindowController];
csvWindowController = [[CSVWindowController alloc] init];
[csvWindowController autorelease];
[self addWindowController:csvWindowController];
}
Problem is I want the second window controller csvWindowController to hide its window initially, I will show the same instance of the window later on. To do so I have written:
#implementation CSVWindowController
- (id) init {
if ( ! (self = [super initWithWindowNibName:#"CSVWindow"]) ) {
NSLog(#"CSVWindowController init failed");
return nil;
}
window = [self window];
NSLog(#"CSVWindowController init");
[window orderOut:nil]; // to hide it
NSLog(#"CSVWindowController hiding the window");
return self;
}
But the window is there, showing up.
Please not I have the VisibleAtLaunch not flagged, that console it's showing my messages correctly, and that even if I change:
[window orderOut:nil]; // to hide it
to
[window orderOut:self]; // to hide it
The result is the same, window showing up.
Any help is appreciated, thanks :)
Ok, again I reply to my own question, but this time with a positive remark. I think what I was doing wrong had something to do with the hidden - for me - implications of the Document-based architecture of the default Document Application template.
I have tried with a different approach, creating an application from scratch NOT flagging "Document-based Application" and providing it with:
1 NSDocument subclass
2 NSWindowControllers subclasses
1 MainMenu.xib
2 window.xib
and I have forced instantiation of the NSWindowController subclasses in the MyDocument code.
I have also put the IBActions for the MenuItems in the MyDocument and I have bound the MyDocument Object to the MenuItems in the MainMenu.xib.
This time I was able to do whatever, hiding/showing windows starting with one hidden one not, enabling menu items automatically at will.
Here follows the code, for any newbie like me who might have to fight with this in the future.
// MyDocument.h
#import <Cocoa/Cocoa.h>
#import "testWindowController.h"
#import "test2WindowController.h"
#interface MyDocument : NSDocument {
testWindowController *test;
test2WindowController *test2;
}
- (IBAction)showWindow1:(id)pId;
- (IBAction)showWindow2:(id)pId;
- (IBAction)hideWindow1:(id)pId;
- (IBAction)hideWindow2:(id)pId;
#end
// MyDocument.m
#import "MyDocument.h"
#import "testWindowController.h"
#import "test2WindowController.h"
#implementation MyDocument
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
NSLog(#"MyDocument init...");
[self makeWindowControllers];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)makeWindowControllers
{
test = [[testWindowController alloc] init];
test2 = [[test2WindowController alloc] init];
[self addWindowController:test];
[self addWindowController:test2];
// start hiding the first window
[[test window] orderOut:self];
}
- (IBAction)hideWindow1:(id)pId
{
NSLog(#"hideWindow1");
[[test window] orderOut:self];
}
- (IBAction)showWindow1:(id)pId
{
NSLog(#"showWindow1");
[test showWindow:self];
[[test window] makeKeyAndOrderFront:nil]; // to show it
}
- (IBAction)hideWindow2:(id)pId
{
NSLog(#"hideWindow2");
[[test2 window] orderOut:self];
}
- (IBAction)showWindow2:(id)pId
{
NSLog(#"showWindow2");
[test2 showWindow:self];
[[test2 window] makeKeyAndOrderFront:nil]; // to show it
}
-(BOOL)validateMenuItem:(NSMenuItem *)menuItem {
NSLog(#"in validateMenuItem for item: %#", [menuItem title]);
if ([[menuItem title] isEqualToString:#"Show Window"]
&& [[test window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Hide Window"]
&& ![[test window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Show Window2"]
&& [[test2 window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Hide Window2"]
&& ![[test2 window] isVisible]){
return NO;
}
return [super validateMenuItem:menuItem];
}
This is another method to prevent NSDocument's window(s) to show up early:
Subclass NSDocuments's window in IB
Use a flag to signal when window content is ready
Override makeKeyAndOrderFront method.
#interface DocWindow : NSWindow
#property BOOL inited;
#end
#implementation DocWindow
- (void)makeKeyAndOrderFront:(id)sender
{
if ( _inited )
[super makeKeyAndOrderFront:sender];
}
#end
#implementation Document
- (void)windowControllerDidLoadNib:(NSWindowController *)windowController
{
// prepare window content here.
...
// show doc's window when ready
DocWindow *win = (DocWindow *)self.window;
win.inited = YES;
[win makeKeyAndOrderFront:self];
}
#end