Receiving Drag events on an NSWindow when the window contains a single WebView view - objective-c

I am new to objective-c and cocoa so please break things down for me. I'm working on a project started by another developer and have only been working in objective-c for 3 days.
I have an NSWindow subclass that contains a WebView view. The WebView content that is loaded is a Silverlight plugin. I registered the NSWindow to receive Drag events. The drag events are being generated but only when the drag occurs within the NSWindow Title Bar. I register for the drag events in the load method.
AdminWindow.mm
#import "AdminWindow.h"
#import "NativeMessageReceiver.h"
extern AdminWindow* adminRiaWindow;
#implementation AdminWindow
#synthesize adminWebView;
BOOL isAdminContentLoaded;
-(void) load
{
if (!isAdminContentLoaded)
{
NSLog(#"loading Admin window");
NSString *curDir = [[NSBundle mainBundle] bundlePath];
NSString* url = [NSString stringWithFormat: #"file://%#/Contents/Resources/RIA/AdminContentMac.html",curDir];
[[adminWebView mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: url]]];
[adminWebView setDrawsBackground:NO];
id win = [adminWebView windowScriptObject];
NativeMessageReceiver* receiver = [NativeMessageReceiver getInstance];
[win setValue:receiver forKey:#"NativeMessageReceiver"];
receiver.adminWebView = adminWebView;
isAdminContentLoaded = YES;
}
}
-(void) show
{
[self load];
[self setIsVisible: YES];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[self makeKeyAndOrderFront: self];
[self makeMainWindow];
[self center];
}
-(void) hide
{
[self setIsVisible: NO];
}
- ( BOOL ) windowShouldClose : ( id ) sender
{
[self setIsVisible: NO];
return NO;
}
- (BOOL) canBecomeKeyWindow
{
return YES;
}
- (BOOL) canBecomeMainWindow
{
return YES;
}
#end
extern "C" void ShowAdminWindow()
{
NSLog(#"showing Admin window");
if (![NSThread isMainThread])
[adminRiaWindow performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
else
{
[adminRiaWindow show];
}
}
extern "C" void HideAdminWindow()
{
if (![NSThread isMainThread])
{
[adminRiaWindow performSelectorOnMainThread:#selector(hide) withObject:nil waitUntilDone:YES];
}
else
{
[adminRiaWindow hide];
}
}
CustomeWebView
#implementation SteamPunkWebView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
NSLog(#"In Custom View Init");
}
return self;
}

I think you're only getting drag events in the title bar because you registered the window to receive the events, not the web view.
Try this instead:
[self.adminWebView registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];

WebViews are one of the most complex views in Cocoa and are a special case when it comes to dragging. Instead of implementing the standard NSView dragging behaviour, you need instead to set an object as the web view's WebUIDelegate and implement the following delegate method:
‑webView:dragDestinationActionMaskForDraggingInfo:
You can then control how you want the web view to respond to the drag action. The WebUIDelegate protocol has several other methods to control dragging, so you should read the documentation for more detail.
To actually receive the dragged objects and process them, you will need to subclass WebView and implement ‑draggingEnded:.
You definitely do not need to add any dragging methods to the window itself.

Related

Unable to Get View Class in the top

I am trying to get current view open in my code using this code in delegate while handling Push Notification
NSArray *views = window.subviews;
NSLog(#"views: %#", views);
UIView *topview=[views lastObject];
when is click on topview and see using below tool (eye mark) it shows the current view
open .
But when i am try to get its kind of class using below two method none of these work
for(UIView *view in window.subviews)
{
if([view isKindOfClass:[MyClass class]])
{
NSLog(#"view is a MyClass\n");
}
}
or
if ([topview isKindOfClass:[MyClass class]])
{
NSLog(#"topview is a MyClass\n");
}
else
{
NSLog(#"topview is NOT a MyClass\n");
}
How can i get my visible view name of kind of class.
self.navigationController.viewControllers returns a UIViewController* array, not a UIView* array.
So, for your first solution:
for(UIViewController *viewController in self.navigationController.viewControllers)
{
if([viewController.view isKindOfClass:[MyClass class]])
{
NSLog(#"view is a MyClass\n");
}
}
will work.
For the visible UIViewController*, you can use:
UIViewController* viewController = self.navigationController.visibleViewController;
For the topmost view:
UIView* topmostView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

UIWebView display PDF and hide gray shadow in iOS7

I'm trying to remove/hide the shadow from an UIWebView that displays a PDF in iOS7.
I've tried all solutions on stackoverflow and also others from the Internet, but it doesn't work.
Maybe it's because I'm using NSURLSession to load a PDF from a server and then display it.
Right now it looks like this:
My first guess was that it doesn't work because the NSURLSession delegates are not on the main thread but even if I remove the subviews (that contain the shadow) on the main thread and call setNeedsDisplay it doesn't change.
I'm starting a DownloadTask and when the task is finished and the delegate gets called I remove the layers.
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
NSData *data = [NSData dataWithContentsOfURL:location];
[self.webView loadData:data MIMEType:#"application/pdf" textEncodingName:#"utf-8" baseURL:nil];
// remove shadow layers from scrollview
dispatch_async(dispatch_get_main_queue(), ^{
self.webView.scalesPageToFit = YES;
for (UIView* subView in [self.webView subviews])
{
if ([subView isKindOfClass:[UIScrollView class]]) {
for (UIView* shadowView in [subView subviews])
{
if ([shadowView isKindOfClass:[UIImageView class]]) {
[shadowView setHidden:YES];
}
}
}
}
[self.webView.layer setNeedsDisplay];
});
}
Even if a remove the GCD async block and it's executed in the same thread it doesn't change anything. I've also tried to call it in viewDidLoad and viewDidAppear.
Any tips are highly appreciated!
- (void)webViewDidFinishLoad:(UIWebView *)webView {
for (UIView *object in webView.scrollView.subviews) {
if ([NSStringFromClass([object class]) isEqualToString:#"UIWebPDFView"]) {
UIView *pdfView = object;
for (UIView *pdfObjectSubview in pdfView.subviews) {
if ([NSStringFromClass([pdfObjectSubview class]) isEqualToString:#"UIPDFPageView"]) {
UIView *uiPDFPageView = pdfObjectSubview;
uiPDFPageView.layer.shadowOpacity = 0.0f;
}
}
}
}
}

How to fixing bad access error in callback using NSMethodSignature?

I am creating a custom modal layer. the idea is a user see's a city, clicks on said city and accepts or rejects the location.
I use NSMethodSignature to handle the callback so I know whether the user clicked Tick (Accept) or Cross (Reject) buttons in the modal layer.
However, for some reason the app crashes once it invokes the callback and gives me a bad access error when it returns from the modal.
0x000d584e <+0174> mov %eax,0x4(%esp)
0x000d5852 <+0178> call 0xd6006 <dyld_stub_objc_msgSend>
0x000d5857 <+0183> mov -0x8(%ebp),%eax Thread 1: Program received signal: EXC_BAD_ACCESS
I do not understand why this is happening as the city object is defined in the class.
For example, my .h file has:
#interface MapMenuLayer : CCLayer
{
CCArray *listOfCities;
City *city;
}
#property (nonatomic, retain) CCArray *listOfCities;
#property (nonatomic, retain) City *city;
And my .m file has:
#synthesize listOfCities, city;
Later on I define the display each city on the page as clickable CCMenu items. When the user clicks on a city it calls:
-(void) onMenuItem:(id)sender
{
NSLog(#"sender = %d", [sender tag]);
self.city = [self.listOfCities objectAtIndex:[sender tag]];
NSString *cityName = self.city.name;
NSLog(#"cityName = %#", cityName);
// Launch the modal layer
CityModalLayer *cityModalLayer = [[[CityModalLayer alloc] initWithCity:self.city target:self selector:#selector(onDialogButton:)] autorelease];
[cityModalLayer show:self];
}
// This is called when the modal is closed or actioned upon
- (void) onDialogButton:(NSInteger)buttonIndex
{
NSLog(#"onDialogButton:buttonIndex: %d", buttonIndex);
NSString *cityName = self.city.name;
NSLog(#"You selected: cityName = %#", cityName);
}
The bad access error occurs when the application flow returns from the modal layer and launches the onDialogButton method is actioned.
It outputs the log fine, but it crashes when it hits the city object. I have no idea why this is happening, it should not be null or causing any formal error.
Okay, so the modal layer is a bit complex, but I cut it down for the purposes of this question:
-(id) initWithCity:(City *)cityObj target:(id)target selector:(SEL)selector
{
if((self=[super init]))
{
[self initWithColor:ccc4(0, 0, 0, 255)];
[self setOpacity:80];
[self setIsTouchEnabled:YES];
self.city = cityObj;
// Setup the signature class
NSMethodSignature *sig = [[target class] instanceMethodSignatureForSelector:selector];
callback = [NSInvocation invocationWithMethodSignature:sig];
[callback setTarget:target];
[callback setSelector:selector];
[callback retain];
// -----
// MENU
// The frames are not in this code, but they do exist
// Add modal menu
// Modal Menu (Tick/Cross)
CCMenu *modalMenu = [CCMenu menuWithItems:nil];
CCSprite *closeButtonOff = [CCSprite spriteWithSpriteFrameName:#"closeButton_Off.png"];
CCSprite *closeButtonOn = [CCSprite spriteWithSpriteFrameName:#"closeButton_On.png"];
CCSprite *tickButtonOff = [CCSprite spriteWithSpriteFrameName:#"tickButton_Off.png"];
CCSprite *tickButtonOn = [CCSprite spriteWithSpriteFrameName:#"tickButton_On.png"];
// Tick button
CCMenuItemSprite *tickBtnItem = [CCMenuItemSprite itemFromNormalSprite:tickButtonOff selectedSprite:tickButtonOn target:self selector:#selector(onButtonPressed:)];
[tickBtnItem setTag:1];
[tickBtnItem setPosition:CGPointMake(130, -95)];
[tickBtnItem setIsEnabled:YES];
// Close button
CCMenuItemSprite *closeBtnItem = [CCMenuItemSprite itemFromNormalSprite:closeButtonOff selectedSprite:closeButtonOn target:self selector:#selector(onButtonPressed:)];
[closeBtnItem setTag:0];
[closeBtnItem setPosition:CGPointMake(-130, -95)];
[closeBtnItem setIsEnabled:YES];
// Add stuff to modal
[modalMenu addChild:closeBtnItem];
[modalMenu addChild:tickBtnItem];
// Add menu to the modalFrame
[modalFrame addChild:modalMenu z:2];
// --
// Add modalFrame to modalLayer
[self addChild:modalFrame];
} // end if
return self;
}
// This invokes the action
-(void) onButtonPressed:(id) sender
{
NSInteger buttonIndex = [sender tag];
NSLog(#"onButtonPressed: %d", buttonIndex);
[callback setArgument:&buttonIndex atIndex:1];
[callback invoke];
[self removeFromParentAndCleanup:YES];
}
-(void) dealloc
{
[callback release];
[super dealloc];
}
I have tracked the issue down to when the callback is invoked, however I do not understand why it is crashing, or giving me the error. It should still have the city object in memory?
What could be causing this?
I've done it now. It was the autorelease. Which I have removed
In order to prevent clicks under the modal, or someone pressing on a button lots of times really quickly I put a CoverLayer inside my ModalLayer.m file
// class that implements a black colored layer that will cover the whole screen
// and eats all touches except within the dialog box child
#interface CoverLayer : CCLayerColor {
}
#end
#implementation CoverLayer
- (id)init {
self = [super init];
if (self) {
[self initWithColor:ccc4(0,0,0,0)
width:[CCDirector sharedDirector].winSize.width
height:[CCDirector sharedDirector].winSize.height];
self.isTouchEnabled = YES;
}
return self;
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace: touch];
CCNode *dialogBox = [self getChildByTag: kDialogTag];
// eat all touches outside of dialog box
return !CGRectContainsPoint(dialogBox.boundingBox, touchLocation);
}
- (void) registerWithTouchDispatcher {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
}
#end
Then I can init this using my ModalLayer implementation and then dismiss the modal with a small animation, ie,
-(void) onButtonPressed:(id) sender
{
NSInteger buttonIndex = [sender tag];
[callback setArgument:&buttonIndex atIndex:2];
[callback invoke];
id fadeOut = [CCFadeTo actionWithDuration:0.2f opacity:0];
id remove = [CCCallFuncND actionWithTarget:self selector:#selector(removeFromParentAndCleanup:) data:(void*)NO];
[self.coverLayer runAction:
[CCSequence actions:
fadeOut, remove,
nil]];
}
Anyway, I believe I resolved the problem as it seemed to relate to autorelease

Resign first responder from separate class

I have a class that makes a keyboard toolbar which has "Next", "Previous", and "Done" buttons on it. Is there a way for this class to know (or find out) what objects are on the screen at any time?
E.g., can it see what the current view is and what the text fields on it are, and then be able to resign the first responder?
If you specifically want to resign first responder without the need to known which view is the first responder you can send resignFirstResponder to "nil" like this:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
This is documented behaviour although I cannot find in the docs right now.
Is there a way for this class to know
(or find out) what objects are on the
screen at the time?
Find the momma view and you can iterate through all the objects on the screen (because they will be UIViews too) like this. Note that you may need to add recursion:
for (UIView *view in mommaView.subviews) {
do something to the view
}
You can start at the Window class and go down from there, asking [view respondsTo:#selector(isFirstResponder) && [view isFirstResponder] on each. Some debugging code that I use might come in handy as a template and also while debugging:
+ (void) dumpWindowFrom:(NSString *) fromText {
[self dumpViews:[[UIApplication sharedApplication] keyWindow] from:fromText];
}
void dumpViewsRecursive(UIView* view, NSString *text, NSString *indent)
{
Class cl = [view class];
NSString *classDescription = [cl description];
// while ([cl superclass]) //restore to print superclass list
// {
// cl = [cl superclass];
// classDescription = [classDescription stringByAppendingFormat:#":%#", [cl description]];
// }
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%d: %# %# %#", (int)view, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
else
NSLog(#"%d: %# %# %# %#", (int)view, text, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViewsRecursive (subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
+ (void) dumpViews: (UIView *) view {
dumpViewsRecursive (( (!view) ? [[UIApplication sharedApplication] keyWindow] : view), #"" ,#"");
}
+ (void) dumpViews: (UIView *) view from:(NSString *) fromText{
dumpViewsRecursive ((!view) ? [[UIApplication sharedApplication] keyWindow] : view, fromText, #"");
}
yes, the methods provided below will be called whenever a textField becomes Active. I think you are looking for
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return 1;
}
or
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
- (void) textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
and if you are looking for a specific textField in your view, you should assign them tags:
textField.tag =1 // for textField 1
textField.tag =2 // for textField 2
// You may check for these tags and then resign specific ones.

NSWindowController not hiding its window at init?

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