I have to two subviews associated with a view. One is a transparent view that handles a right click, the other a nsview with an nsimageview subview. For some reason the right click works over any part of the superview except the part within the nsimageview. The transparent view is on top of the other view yet the right mouse down event is not firing.
I finally solved it by subclassing the image view and overriding the hit test method to return nil. The full implementation is below :
#implementation TTBaseImageView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[super drawRect:dirtyRect];
}
-(BOOL)isFlipped
{
return YES;
}
-(BOOL)acceptsFirstResponder
{
return NO;
}
-(BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return NO;
}
-(NSView *)hitTest:(NSPoint)aPoint
{
return nil;
}
#end
Related
I have an NSBox subclass called dragBox. I want to be able to drag it around a canvas. The code is as follows:
-(void) awakeFromNib
{
[[self superview] registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
}
-(void) mouseDown:(NSEvent *)theEvent
{
[self dragImage:[[NSImage alloc] initWithContentsOfFile:#"/Users/bruce/Desktop/Untitled-1.png"] at:NSMakePoint(32, 32) offset:NSMakeSize(0,0) event:theEvent pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] source:self slideBack:YES];
}
-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender // validate
{
NSLog(#"Updated");
return [sender draggingSourceOperationMask];
}
-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSLog(#"Drag Entered");
return [sender draggingSourceOperationMask];
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSLog(#"Move Box");
[self setFrameOrigin:[sender draggingLocation]];
return YES;
}
-(BOOL) prepareForDragOperation:(id<NSDraggingInfo>)sender
{NSLog(#"Prepared");
return YES;
}
Why isn't dragEntered being called? I have tried to use all the pboard types and such. Nothing seems to work. I have also changed the registerForDraggedTypes to just work off of the [self] view. The box is a subview of a canvas.
Bruce
I found that awakeFromNib was the wrong place to put my registerForDragTypes call since I am programmatically adding my view (i.e. not adding it via a Nib). I had to put the call into initWithFrame:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes: [NSArray arrayWithObjects:NSTIFFPboardType,NSFilenamesPboardType,nil]];
}
return self;
}
Bruce,
Your Code needs to be changed in the below way. I believe that view should be registered for drag types to make the method draggingEntered to get called.
#interface NSModifiedBox : NSBox
#end
#implementation NSModifiedBox
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSTIFFPboardType,NSFilenamesPboardType,nil]];
[super drawRect:dirtyRect];
}
- (NSDragOperation)draggingEntered:(id )sender
{
if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
== NSDragOperationGeneric)
{
return NSDragOperationGeneric;
} // end if
// not a drag we can use
return NSDragOperationNone;
}
- (BOOL)prepareForDragOperation:(id )sender
{
return YES;
}
#end
Now Drag and Drop a NSBox on the Xib and the Modify the class of NSBox to NSModifiedBox.
Set a break point to the method "draggingEntered".
Now Drag a ".png" or ".gif" file and drop on the NSModifiedBox and you see the "draggingEntered" will get invoked
Or you can check by using NSLog as well inside a "draggingEntered".
Hope my answer will help you :)
I'm subclassing NSButton because I need to repeat a selector while the mouse is being held down.
I'm doing that like this:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
[self setBezelStyle:NSBezelBorder];
PotRightIsDown = NO;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
}
- (void)mouseDown:(NSEvent *)theEvent;
{
NSLog(#"pot right mouse down");
PotRightIsDown = YES;
holdDownTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(sendCommand) userInfo:nil repeats:YES];
}
- (void)mouseUp:(NSEvent *)theEvent;
{
NSLog(#"pot right mouse up");
PotRightIsDown = NO;
}
-(void)sendCommand
{
if (PotRightIsDown)
{
NSLog(#"run the stuff here");
}
else
{
[holdDownTimer invalidate];
}
}
Works like a champ, sends the command every 100ms.
In the window in IB, I've dragged a Bevel Button onto the window and set it's class to this subclass. When I ran the application, the button is invisible however it works. I'm guessing this is because I have an empty drawRect function in the subclass.
How can I make this subclassed button look like a Bevel button?
Thank you,
Stateful
If you aren't adding any functionality to a particular subclass method then you can simply avoid implementing it altogether, which will allow the superclass to provide the default behaviour.
Alternatively (as pointed out my #Carl Norum) you can explicitly do that using:
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
}
But it's a bit pointless.
I've made an application where you shake the phone to open a new view. All together three views, and when you shake the phone on the last view you go back to the first screen. This works fine when I'm creating new subclass controls view with their own .xib. But I would like to use this in a storyboard project, what do I need to change?
Thanks a lot on beforehand!
HERE IS THE CODE IN .H:
#import <UIKit/UIKit.h>
#import "FirstScreenViewController.h"
#import "SecondScreenViewController.h"
#interface ViewController : UIViewController
{
NSInteger currentScreen;
UIViewController* currentController;
}
#end
AND HERE IN THE .M:
#import "ViewController.h"
#implementation ViewController
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark shake
-(BOOL)canBecomeFirstResponder
{
return true;
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(motion == UIEventSubtypeMotionShake)
{
if (currentController)
{
[currentController.view removeFromSuperview];
currentController=nil;
}
switch (currentScreen)
{
case 0:
currentController = [[FirstScreenViewController alloc] initWithNibName:#"FirstScreenViewController" bundle:nil];
break;
case 1:
currentController = [[SecondScreenViewController alloc] initWithNibName:#"SecondScreenViewController" bundle:nil];
}
if(currentController)
{
[currentController.view setFrame:self.view.bounds];
[self.view addSubview:currentController.view];
}
currentScreen++;
if(currentScreen >2)
currentScreen=0;
}
}
#pragma mark - View lifecycle
-(void)viewDidLoad
{
[super viewDidLoad];
currentScreen = 0;
}
-(void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#end
You need to add all three view controllers to the storyboard, and have segues between them (including one back to the first from the third) and a shake gesture recogniser attached to each scene.
The action method for each gesture recogniser tells the view controller to performSegue: with the appropriate segue identifier.
I have an NSStatusItem that has an NSMenuItem which contains a custom NSView. this NSView contains 3 Subviews of NSBox which are clickable (implemented the mouseDown event). Strangely, when I run the application and I enter the statusItem right after I pressed the 'Play'-Button I have to click the NSBox just one time (this returns clickCount = 1). When I enter another window or view and I go back to the statusItem and I try to click one of the NSBoxes nothing happens. When I double click the items, the actions happen and the clickCount is also equal to 1.
-(void)mouseDown:(NSEvent *)theEvent {
NSLog(#"ClickCount: %ld", theEvent.clickCount);
if ([delegate respondsToSelector:#selector(boxClicked:)]) {
[delegate boxClicked:self];
}
}
Does anyone have any idea why this is happening and how I can solve this?
EDIT (full m-file code):
#import "ClickableBox.h"
#implementation ClickableBox
#synthesize delegate;
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
- (void)viewDidMoveToWindow {
[self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO];
}
-(void)mouseEntered:(NSEvent *)theEvent {
if ([delegate respondsToSelector:#selector(boxRolledOver:)]) {
[delegate boxRolledOver:self];
}
}
-(void)mouseExited:(NSEvent *)theEvent {
if ([delegate respondsToSelector:#selector(boxExited:)]) {
[delegate boxExited:self];
}
}
-(void)mouseDown:(NSEvent *)theEvent {
NSLog(#"ClickCount: %ld", theEvent.clickCount);
if ([delegate respondsToSelector:#selector(boxClicked:)]) {
[delegate boxClicked:self];
}
}
- (void)dealloc {
[delegate release];
[super dealloc];
}
#end
It does sound like you also need to do this:
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
so that first mouse-click events are accepted in your custom NSView.
There may be other issues, but can't tell from the code-snippet as-is.
This could be because your view is not key. You can set a delegate for your NSMenu and on -menuWillOpen: you can call [boxView.window makeFirstResponder:boxView]; to make it key.
im very new in cocoa development and I'm trying to load a Window.
I will explain my problem.
When the user click the menuItem I use the following code to load my window
if ( !cadastroContasController )
{
cadastroContasController = [[cadastroContas alloc]init];
[cadastroContasController SetMenuItem:sender];
}
if ( ![[cadastroContasController window] isVisible] )
{
NSLog(#"!isVisible");
[cadastroContasController showWindow:nil];
}
I my cadastroContas class looks like this:
#interface cadastroContas : NSWindowController
{
NSMenuItem *mnuCommand;
IBOutlet NSComboBox *cmbSelecao;
IBOutlet NSTextField *txtNome;
IBOutlet NSTextField *txtSaldoInicial;
IBOutlet NSTextField *txtAnotacoes;
}
- (void)windowDidBecomeKey:(NSNotification *)notification;
- (BOOL)windowShouldClose:(id)sender;
- (void)windowWillClose:(NSNotification *)notification;
- (void)SetMenuItem:(NSMenuItem*) menu;
- (NSMenuItem*) MenuItem;
#end
and the implementation is
#implementation cadastroContas
-(void)windowDidLoad
{
NSLog(#"windowDidLoad");
[mnuCommand setState:NSOnState];
}
-(id)init
{
self = [super initWithWindowNibName:#"cadastroContas"];
NSLog(#"Init self=%p", self);
return self;
}
-(void)dealloc
{
NSLog(#"Dealoc=%p", self);
[super dealloc];
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
NSLog(#"windowDidBecomeKey window=%p", [self window]);
}
- (BOOL)windowShouldClose:(id)sender
{
NSLog(#"windowShouldClose Window=%p", [self window]);
NSLog(#"mnuComando=%p GetMenuItem=%p", mnuCommand, [self MenuItem] );
if ( mnuCommand )
{
[mnuCommand setState:NSOffState];
}
return YES;
}
- (void)windowWillClose:(NSNotification *)notification
{
NSLog(#"windowWillClose Window=%p", [self window]);
NSLog(#"mnuCommand=%p GetMenuItem=%p", mnuCommand, [self MenuItem] );
[self dealloc];
}
- (void)SetMenuItem:(NSMenuItem*) menu
{
mnuCommand = menu;
}
- (NSMenuItem*) MenuItem
{
return mnuCommand;
}
#end
When the menu was clicked, I received two messages "Init" and I don't know why.
Exemple:
[2223:a0f] Init self=0x10014fe40
[2223:a0f] Init self=0x10011f5a0
The second message let the "[cadastroContasController SetMenuItem:sender];" useless.
So, I need help to understand whats going on..
Another thing, [[cadastroContasController window] is always returning NULL(0x0)!!, but inside my controller i can handle it (it isn't null).
This means you inited two instances, as shown by your logging of the self pointer: Notice that the value is different between the two messages.
You can use the Allocations instrument in Instruments to see what caused each window controller to be instantiated.
Usually, this problem happens when you create one of these in the nib and the other one in code. In the case of a window controller, the one you create in code should be the owner of its nib; you should not create another window controller as an object in the nib.
Another thing, [[cadastroContasController window] is always returning NULL(0x0)!!, but inside my controller i can handle it (it isn't null).
The window controller whose window outlet you set to the window is the one that is returning non-nil. The window controller whose window outlet you didn't set is the one that is returning nil.
Following from what I said above, after deleting the window controller you created in the nib, you should connect your File's Owner's window outlet to the window.