cocoa Drag and Drop not registering - objective-c

I am making a simple drag and drop app and while checking, the drag operation is not getting registered. I dropped an NSView on MainMenu.xib and then subclassed that NSView.
DropView.h
#import <Cocoa/Cocoa.h>
#interface DropView : NSView <NSDraggingDestination>
#property (nonatomic, strong) NSImage *image;
#end
DropView.m
#import "DropView.h"
#implementation DropView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes: [NSImage imagePasteboardTypes]];
}
return self;
}
- (NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender{
if ([NSImage canInitWithPasteboard:[sender draggingPasteboard]] && [sender draggingSourceOperationMask] & NSDragOperationCopy ) {
return NSDragOperationCopy;
}
return NSDragOperationNone;
}
- (NSDragOperation) draggingUpdated:(id<NSDraggingInfo>)sender{
NSLog(#"DRAGGING!");
return NSDragOperationCopy;
}
-(void) draggingEnded:(id<NSDraggingInfo>)sender{
NSLog(#"ENDED!");
}
-(void) draggingExited:(id<NSDraggingInfo>)sender{
NSLog(#"EXITED");
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
#end
When I run this, and try to drop an image, the cursor is not changing to the + signed one. and the methods are not getting logged. Any idea?

If you're dragging image files onto your window, (?) then I think you want NSFilenamesPboardType :
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];

Related

NSTabView blocking file-drop events active on underlying Window.

I have successfully implemented a file-drop functionality in my app. The Application window has a few NSTabView objects where dropping on them does not work. Anywhere else in the window the file-drop works fine.
I have tried to make the app delegate a delegate for the NSTabView, but this did not help.
Anyone have a setup for the NSTabView not to filter out the drop-actions so the whole window can be transparent to the file-drop actions ?
For a more generic solution than olekeh's I made it IB friendly so you can hook it up to any object that complies with the NSDraggingDestination protocol.
#import <Cocoa/Cocoa.h>
#interface DropFilesView : NSView
#property (nullable, assign) IBOutlet id<NSDraggingDestination> dropDelegate;
#end
#implementation DropFilesView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
-(void) awakeFromNib {
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType,
(NSString *)kPasteboardTypeFileURLPromise,kUTTypeData, NSURLPboardType, nil]]; //kUTTypeData
[super awakeFromNib];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender{
return [self.dropDelegate draggingEntered:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
return [self.dropDelegate performDragOperation:sender];
}
#end
I found the solution to this !! - I am posting it here for others who might need.
The NSTabView object has for each of its tabs an NSTabViwItem.
Under each of those, there is a regular NSView - that I subclassed with the following code: - The code assumes that you already have "draggingEntered" and "performDragOperation" in your AppDelegate as this class just forwards these messages to the app delegate. You will also need to put the declarations for those methods in you AppDelegate.h
// DropFilesView.h
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface DropFilesView : NSView
#end
and the implementation:
// DropFilesView.m
#import "DropFilesView.h"
#implementation DropFilesView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
-(void) awakeFromNib {
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType,
(NSString *)kPasteboardTypeFileURLPromise,kUTTypeData, NSURLPboardType, nil]]; //kUTTypeData
[super awakeFromNib];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
AppDelegate* del = [AppDelegate sharedAppDelegate];
return [del draggingEntered:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
AppDelegate* del = [AppDelegate sharedAppDelegate];
return [del performDragOperation:sender];
}
#end
In Interfacebuilder, I set the new class for all the NSView objects covering areas where drop does not work, to this new one.
A similar approach can be used for NSImageView and the WebView classes. However, for the last one, do not use [super awakeFromNib] to prevent the default drag-and drop handling for the web view object.

How to get canDisplayBannerAds producing viewDidLayoutSubview callbacks?

In my code, when I set canDisplayBannerAds=YES on my view controller, I get callbacks to viewDidLayoutSubviews when the ad disappears but not when the ad appears. I'm guessing this is because Apple moves the original self.view of the view controller to self.originalContentView when you set canDisplayBannerAds to YES.
My question is, what is a reasonable work around for this?
My solution to this problem is to replace self.view before setting canDisplayBannerAds=YES with a UIView that overrides layoutSubviews.
#protocol LayoutViewDelegate <NSObject>
- (void) layout;
#end
#interface LayoutView : UIView
#property (nonatomic, weak) id<LayoutViewDelegate> delegate;
#end
#implementation LayoutView
- (void) layoutSubviews {
[super layoutSubviews];
if (self.delegate) [self.delegate layout];
}
#end
I do this replacement in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Calendar.viewDidLoad");
// Replace the view, before setting up ads for iOS7, so we can get callbacks for viewDidLayoutSubviews; otherwise, we only get viewDidLayoutSubviews callbacks when ad disappears.
if ([Utilities ios7OrLater]) {
self.layoutView = [[LayoutView alloc] initWithFrame:self.view.frame];
self.view = self.layoutView;
self.layoutView.delegate = self;
}
}
And in viewDidAppear, I do:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([Utilities ios7OrLater]) {
self.canDisplayBannerAds = YES;
}
}
And I add the delegate method:
// This *always* gets called when the banner ad appears or disappears.
#pragma - LayoutViewDelegate method
- (void) layout {
// do useful stuff here
}
#pragma -

Changing UIAccelerometer's updateInterval via a UISlider

I'm trying to change my UIAcceleromter's updateInterval via a UISlider that I have in my .xib. My NSLogs shows me the values perfectly, but the acceleromter's interval does not increase in speed when I move the slider. I'm not sure what's wrong.
I appreciate any help offered. Here is my code:
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIAccelerometerDelegate> {
}
- (IBAction)sliderValueChanged:(UISlider *)slider;
#end
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize XAxis, YAxis, delta;
float value;
- (void)viewWillAppear:(BOOL)animated
{
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f * value;
NSLog(#"Value: %f",value);
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
NSLog(#"X: %g",acceleration.x);
NSLog(#"Y: %g",acceleration.y);
NSLog(#"Z: %g",acceleration.z);
}
- (IBAction) sliderValueChanged:(UISlider *)slider
{
value = slider.value;
NSLog(#"Value: %f",slider.value);
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#end
There isn't a line of code that changes you accelerometer's update interval. All you are doing is remembering the value of the slider in a float and outputting it via NSLog.
The accelerometers value is only being set when your view appears.
You need to store the accelerometer as a property of your object and do something like
self.accelerometer.updateInterval = value;
in your sliderValueChanged: method.

SubClassing UILabel

I read in this same site how to inset and UILabel (subclass UILabel and override the required methods). Before adding it to my app I decided to test it out in a standalone test app. Code is shown below.
Here's MyUILabel.h
#import <UIKit/UIKit.h>
#interface MyUILabel : UILabel
#end
Here's MyUILabel.m
#import "MyUILabel.h"
#import <QuartzCore/QuartzCore.h>
#implementation MyUILabel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// for border and rounding
-(void) drawRect:(CGRect)rect
{
self.layer.cornerRadius = 4.0;
self.layer.borderWidth = 2;
[super drawRect:rect];
}
// for inset
-(void) drawTextInRect:(CGRect)rect
{
UIEdgeInsets insets = {0, 5, 0, 5};
[super drawTextInRect: UIEdgeInsetsInsetRect(rect, insets)];
}
Here's my ViewController.h
#import <UIKit/UIKit.h>
#import "MyUILabel.h"
#interface ViewController : UIViewController
{
MyUILabel *myDisplay;
}
#property (strong, nonatomic) IBOutlet MyUILabel *myDisplay;
#end
Here's ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myDisplay;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myDisplay.text = #"Hello World!";
}
- (void)viewDidUnload
{
[self setMyDisplay:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
None of the methods in MyUILabel.m (that Im overriding) get called.
Insights into why are greatly appreciated.
Regards,
Ramon.
Ok. I did some further digging and in Xcode there is a field visible when looking at the nib file. Its the 'Identity Inspector' (3rd icon from left). This needed to be changed from UILabel to MyUILabel.
Now it works!

changing UILabel text on a subview from main view

Ok, so I'm a relative noob with Objective-C/iOS programming, so hopefully someone with more knowledge here can help me out.
I have an iPad application using the SplitViewController template (with Core Data). I created another UIViewController (with xib file) called PlayerViewController. This View has several UILabel components on it.
I have a list of players that show up in the RootViewController (UITableView) and when you select a player, I programmatically create a PlayerViewController (in DetailViewController), pass it the NSManagedObject that was passed to the DetailViewController, try to set the text of one of the labels on the PlayerViewController's view, and then add it as a subview to the DetailViewController.
All of this works great except for the setting the text of the label on the PlayerViewController's view. I'm not sure what I'm doing wrong. I have used NSLog to confirm that the NSManagedObject is not nil and that the NSManagedObject property I'm trying to use has the correct text.
I'm at a loss here. Any help would be greatly appreciated. (Code follows):
This method is in the DetailViewController.m file:
- (void)configureView {
// Update the user interface for the detail item.
PlayerViewController *player = [[PlayerViewController alloc] init];
player.player = detailItem;
[self.view addSubview:player.view];
}
This method is called when the user selects an item from the RootViewController (This functionality, calling of configureView, is setup by the template and I haven't changed it).
Setting the player property of the PlayerViewController to object detailItem is handled in the setPlayer method of that class.
- (void)setPlayer:(NSManagedObject *)managedObject {
if (player != managedObject) {
[player release];
player = [managedObject retain];
// Update the view.
[self configureView];
}
}
I then have a configureView method as well in PlayerViewController that sets the text of the label:
- (void)configureView {
nickName.text = [[player valueForKey:#"Nickname"] description];
NSLog(#"Nickname %#", [[player valueForKey:#"Nickname"] description]);
NSLog(#"Nickname %#", nickName.text);
}
Ok, so the first NSLog statement prints the desired value, but the text of the UILabel (called nickName) returns nil.
The following is the full PlayerViewController.h & .m files:
PlayerViewController.h:
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface PlayerViewController : UIViewController {
NSManagedObject *player;
IBOutlet UILabel *nickName;
IBOutlet UILabel *goalCount;
IBOutlet UILabel *assistCount;
IBOutlet UILabel *timeInGame;
}
#property (nonatomic, retain) IBOutlet UILabel *nickName;
#property (nonatomic, retain) IBOutlet UILabel *goalCount;
#property (nonatomic, retain) IBOutlet UILabel *assistCount;
#property (nonatomic, retain) IBOutlet UILabel *timeInGame;
#property (nonatomic, retain) NSManagedObject *player;
#end
PlayerViewController.m:
#import "PlayerViewController.h"
#implementation PlayerViewController
#synthesize nickName, goalCount, assistCount, timeInGame, player;
#pragma mark -
#pragma mark Managing the detail item
/*
When setting the player item, update the view
*/
- (void)setPlayer:(NSManagedObject *)managedObject {
if (player != managedObject) {
[player release];
player = [managedObject retain];
// Update the view.
[self configureView];
}
}
- (void)configureView {
nickName.text = [[player valueForKey:#"Nickname"] description];
NSLog(#"Nickname %#", [[player valueForKey:#"Nickname"] description]);
NSLog(#"Nickname %#", nickName.text);
}
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
I'm sure I'm just missing something trivial, but I can't figure it out, and haven't been able to find any answers searching the web.
Thanks for any help!
Ok, so after playing with this for a bit and searching and searching around, I have gotten the answer to my problem. It turns out all the code I had was fine except the location of one statement. My call to configureView in PlayerViewController.m needed to be in viewDidLoad() not in the setPlayer() method. It all works great now.
Change the configureView method to that :
- (void)configureView {
nickName.text = (NSString*)[player valueForKey:#"Nickname"];
}
Yes, better place to call method is
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self configureView];
}
(void)setPlayer:(NSManagedObject *)managedObject called before your nib files loaded.