SpriteKit presentScene crash app - ios8

I am navigating between SK scenes using the following code:
SKScene *newScene = [[MyScene alloc] initWithSize:self.size state:state];
float duration = 1;
SKTransition *transition = [SKTransition fadeWithDuration:duration];
[self.view presentScene:newScene transition:transition];
Regardless the type of scene I am trying to present, the app crashes every time with EXC_BAD_ACCESS (code 1) without any zombies.. It looks like the "present scene won't work correctly on xcode6, it did work before I upgraded from xcode5
edit: this is the initWithSize code:
-(id)initWithSize:(CGSize)size state:(GameState)state{
if (self = [super initWithSize:size]) {
[self setupScene];
if (state == GameStateLaunch) {
[self setupLoadingScreen];
[self switchToLaunch];
} else if (state == GameStateTutorial || state == GameStateReplay) {
[self switchToTutorial];
} else if (state== GameStateMainMenu) {
[self switchToMainMenu];
}
}
return self;
}
I managed to make it work partially by removing all children before presenting the new scene:
- (void)cleanUpChildrenAndRemove {
for (SKNode *child in self.children) {
[child removeFromParent];
}
[self removeFromParent];
}
However, this is crazy. There must be a better solution!

Related

Expected identifier or "(" error

EDITED: Thanks to your help I am closing in on the solution to my problem. I have fixed the missing "#end error", and am left with just one "Expected identifier or "("" error.
This is the code, the error is noted:
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] \
compare:v options:NSNumericSearch] == NSOrderedAscending)
#import "WViewController.h"
#import <SkillzSDK-iOS/Skillz.h>
#interface WViewController ()
#end
#implementation WViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//create backdrop image
NSMutableString *imgFile = [[NSMutableString alloc] init];
if (![globalBackgroundImage isEqualToString:#""]) [imgFile setString:globalBackgroundImage]; //falls back to first image setting for all devices;
if ((IS_WIDESCREEN_PHONE)&&(![widescreenBackgroundImage isEqualToString:#""])) [imgFile setString:widescreenBackgroundImage]; //you can specify a different image for
if ((IS_IPAD)&&(![iPadBackgroundImage isEqualToString:#""])) [imgFile setString:iPadBackgroundImage]; //widescreen phones & for iPad
if (![imgFile isEqualToString:#""]) {
UIImage *img = [[UIImage imageNamed:imgFile] retain];
CGSize imgSz = [img size];
CGSize screenSz = [[UIScreen mainScreen] bounds].size;
float imgWH = imgSz.width/imgSz.height;
float screenWH = screenSz.width/screenSz.height;
CGRect backdropFrame;
if (imgWH>=screenWH) backdropFrame = CGRectMake((screenSz.width/2)-((screenSz.height*imgWH)/2), 0, screenSz.height*imgWH, screenSz.height); //image wider than screen
else backdropFrame = CGRectMake(0, ((screenSz.height/2)-((screenSz.width/imgWH)/2)), screenSz.width, screenSz.width/imgWH);
UIImageView *backdropImageView = [[UIImageView alloc] initWithFrame:backdropFrame];
[backdropImageView setImage:img];
[backdropImageView setAlpha:backgroundImageOpacity];
[self.view addSubview:backdropImageView];
}
[self.view setBackgroundColor:globalBackgroundColor];
//init GameCenter
[[GameCenterManager sharedManager] setupManager];
[[GameCenterManager sharedManager] setDelegate:self];
//initialize the view for when the player is in the game
gameView = [[WgameView alloc] initWithFrame:[[UIScreen mainScreen] bounds] fromViewController:self];
[gameView setHidden:YES];
[self.view addSubview:gameView];
//initialize the view for then the player is on the home screen
homeView = [[WhomeView alloc] initWithFrame:[[UIScreen mainScreen] bounds] fromViewController:self];
[homeView setHidden:YES];
[self.view addSubview:homeView];
}
- (void) viewDidAppear:(BOOL)animated {
//go to home screen right away
[self goHome];
//show a RevMob fullscreen ad if we're supposed to
if (revMobActive) {
if (showRevMobFullscreenOnLaunch) {
[[RevMobAds session] showFullscreen];
}
}
//show a Chartboost ad if we're supposed to
if (chartboostActive) {
if (showChartboostOnLaunch) {
[[Chartboost sharedChartboost] showInterstitial:CBLocationHomeScreen];
}
}
}
#pragma mark game flow
-(void) multiplayerButtonPressed:(id)sender
{
NSLog(#"Multiplayer button pressed, launching Skillz!");
// Launching Skillz in landscape mode
[[Skillz skillzInstance] launchSkillzForOrientation:SkillzLandscape
launchHasCompleted:^{
// This code is called after the Skillz UI launches.
NSLog(#"Skillz just launched.");
} tournamentWillBegin:^(NSDictionary *gameRules) {
// This code is called when a player starts a game in the Skillz portal.
NSLog(#"Tournament with rules: %#", gameRules);
NSLog(#"Now starting a game…");
// INCLUDE CODE HERE TO START YOUR GAME
// …..
// …..
// …..
// END OF CODE TO START GAME
} skillzWillExit:^{
// This code is called when exiting the Skillz portal
//back to the normal game.
NSLog(#"Skillz exited.");
}];
}
- (void) startGame:(UIButton*)sender {
//hide RevMob banner ad if we're supposed to
if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] hideBanner];
}
}
//starts game in the mode corresponding to which button was tapped
[[WGameModeEngine sharedInstance] setCurrentGameMode:sender.titleLabel.text];
[gameView startGame];
[homeView setHidden:YES];
[gameView setHidden:NO];
//init timer if timed game
if ([[WGameModeEngine sharedInstance] isTimedGame]) {
timedGameTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(decrementTime) userInfo:nil repeats:YES] retain];
}
//notify game engine to play sound if configured
[[WGameModeEngine sharedInstance] soundEventDidHappen:#"BeginGame"];
}
- (void) goHomeButtonPressed {
[self stopGame];
[self goHome];
}
- (void) stopGame {
//stop timer if this was a timed game
if (timedGameTimer) {
[timedGameTimer invalidate];
[timedGameTimer release];
timedGameTimer=nil;
}
}
- (void) goHome {
[gameView setHidden:YES];
[homeView setHidden:NO];
//show a RevMob banner ad if we're supposed to
if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] showBanner];
}
}
}
- (void) decrementTime {
[[WGameModeEngine sharedInstance] timeDecreased]; //report to our game model that time has decreased
if ([[WGameModeEngine sharedInstance] timeLeft]<=0) { //if 0 seconds left,
[self timedGameEnded]; //game has ended
}
if (([[WGameModeEngine sharedInstance] timeLeft]<6)&&([[WGameModeEngine sharedInstance] timeLeft]>0)) {
//notify game engine to play sound if configured
[[WGameModeEngine sharedInstance] soundEventDidHappen:#"FiveSecondCountdown"];
}
[gameView updateLabels]; //update gameView's score and time labels
}
- (void) timedGameEnded {
//game over!
[self stopGame];
//notify game engine to play sound if configured
[[WGameModeEngine sharedInstance] soundEventDidHappen:#"GameOver"];
//show an alert with score and list of words found (if you want, you can add a whole separate screen for this instead of simple alert!)
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Game Over" message:[NSString stringWithFormat:#"You scored %d points!\n\nWords found:\n%#",[[WGameModeEngine sharedInstance] getScore],[[[WGameModeEngine sharedInstance] getWordsFound] componentsJoinedByString:#" "]] delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert setDelegate:self];
[alert show];
[alert release];
//report score to GameCenter
int sc = [[WGameModeEngine sharedInstance] getScore];
if (sc>0) [self reportScore:sc forCategory:[[[[WGameModeEngine sharedInstance] getCurrentGameMode] componentsSeparatedByString:#" "] componentsJoinedByString:#"_"]];
[#"com.bundle.appname" stringByAppendingString:[[[[[WGameModeEngine sharedInstance] getCurrentGameMode] lowercaseString] componentsSeparatedByString:#" "] componentsJoinedByString:#""]];
}
- (void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
//share latest score on Facebook if we're supposed to
if (FacebookShareEnabled) {[self facebookShare];}
//go to home screen
[self goHome];
//show a RevMob fullscreen ad if we're supposed to
if (revMobActive) {
if (showRevMobFullscreenWhenGameOver) {
[[RevMobAds session] showFullscreen];
}
}
//show a Chartboost ad if we're supposed to
if (chartboostActive) {
if (showChartboostWhenGameOver) {
[[Chartboost sharedChartboost] showInterstitial:CBLocationHomeScreen];
}
}
}
#pragma mark GameCenter
- (void)gameCenterManager:(GameCenterManager *)manager authenticateUser:(UIViewController *)gameCenterLoginController {
if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] hideBanner];
}
}
[self presentViewController:gameCenterLoginController animated:YES completion:^(void)
{if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] showBanner];
}}}];
}
if (isGameOver) {<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ERROR IS HERE>>>>>>>>>>>>>>>
if ([[Skillz skillzInstance] tournamentIsInProgress]) {
// The game ended and it was in a Skillz tournament,
// so report the score and go back to Skillz.
[[Skillz skillzInstance] completeTurnWithGameData:gameData
playerScore:playerScore
playerCurrentTotalScore:playerCurrentTotalScore
opponentCurrentTotalScore:opponentCurrentTotalScore
roundOutcome:turnOutcome
matchOutcome:matchOutcome
withCompletion:^{
// Code in this block is called when exiting to Skillz
// and reporting the score.
NSLog(#"Reporting score to Skillz…");
}];
} else {
// Otherwise single player game, so take the normal action
}
}
- (void) reportScore: (int64_t) score forCategory: (NSString*) category
ORIGINAL QUESTION:
I am really new to coding and hoping that someone can help me with what I am sure is a very simple problem. I have looked at a lot of other answers regarding this error, but they don't seem to apply to my situation.
The following code is part of a game app I am working on using Xcode, trying to integrate it with a third party system called SKILLZ. I did not write any of the code and am trying to understand it as I proceed with the integration.
I have noted within the code where I am getting the error:
#import "WAppDelegate.h"
#import "WViewController.h"
#import <SkillzSDK-iOS/Skillz.h>
#implementation WAppDelegate
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//initialize language engine
[WLanguageEngine sharedInstance];
//initialize game mode engine
[WGameModeEngine sharedInstance];
//initialize RevMob
if (revMobActive) {
[RevMobAds startSessionWithAppID:RevMobAppID];
}
if (revMobActive&&revMobTestingMode) {
[RevMobAds session].testingMode = RevMobAdsTestingModeWithAds;
// or
//[RevMobAds session].testingMode = RevMobAdsTestingModeWithoutAds;
}
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[WViewController alloc] initWithNibName:#"WViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<**<<EXPECTED IDENTIFIER OR "(">> ERROR OCCURS HERE**
// INITIALIZE SKILLZ HERE
// 940 is the game ID that was given to us by the Skillz Developer Portal.
// SkillzSandbox specifies that we will use the sandbox server since we
// are still developing the game.
// SkillzProduction specifies that we will use the production server since
// the game is ready for release to the AppStore
[[Skillz skillzInstance] skillzInitForGameId:#"940"
environment:SkillzSandbox];
I am getting a second occurrence of this error in a different part of the app code, but am hoping that if I can sort this one out that it will help me to understand the other.
Hoping that this is not off-topic or too specific, and that someone might be able to help.
Cheers
Jen
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ERROR OCCURS HERE
// INITIALIZE SKILLZ HERE
// 940 is the game ID that was given to us by the Skillz Developer Portal.
// SkillzSandbox specifies that we will use the sandbox server since we
// are still developing the game.
// SkillzProduction specifies that we will use the production server since
// the game is ready for release to the AppStore
[[Skillz skillzInstance] skillzInitForGameId:#"940"
environment:SkillzSandbox];
return YES;
}
This block is outside of - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Perhaps you forgot a declaration or added an extraneous } somewhere

Cocoa - How to distinguish between a click and scroll(continuous press of UP/DOWN key) inside a NSTableView

I want to fire a query when a particular record gets selected in an NSTableView, not when the user just scrolls down or scrolls up by continuously pressing UP/DOWN button to reach to a record.
My current implementation is
if ([notification object] == myTableView)
{
if ([myTableView selectedRow] >= 0) {
myCont = [[MyController alloc] init];
if([[detailsView subviews]count]>0)
[detailsView removeAllSubviews];
NSRect frameRect = [[scDetailsViewController view] frame];
frameRect.size.height = [detailsView frame].size.height;
frameRect.size.width = [detailsView frame].size.width;
[[myCont view] setFrame:frameRect];
[detailsView addSubview:[myCont view]];
//Firing the Query
[myCont populateDetails :[[self myList] entityAt:[myTableView selectedRow]]];
}
}
But in this way the query gets fired even if a long UP/DOWN press is done which is not intended.
Is there any way to distinguish between a click and scroll(continuous press of UP/DOWN key) inside an NSTableView just like the Mail application.
Just wrote the below piece of code in the tableViewSelectionDidChange method and that almost works perfectly.
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
if ([notification object] == myTableView)
{
NSTimeInterval delayInterval = 0.0;
NSEvent *event = [NSApp currentEvent];
if(event != nil && [event type] == NSKeyDown && [event isARepeat])
{
NSLog(#"Long press of UP and DOWn arrow key.");
delayInterval = [NSEvent keyRepeatInterval] + 0.01;
}
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(myMethod) object:nil];
[self performSelector:#selector(myMethod) withObject:nil afterDelay:delayInterval];
}
else
{
if ([[detailsView subviews] count]>0)
{
[detailsView removeAllSubviews];
}
}
}
Also written the code to fire the query in another method myMethod which is being called above in performSelector.
-(void) myMethod
{
if ([scenarioTableView selectedRow] >= 0) {
NSLog(#"Normal selection on table view row.");
scDetailsViewController = [[ScenarioDetailsViewController alloc] init];
if([[detailsView subviews]count]>0)
[detailsView removeAllSubviews];
NSRect frameRect = [[scDetailsViewController view] frame];
frameRect.size.height = [detailsView frame].size.height;
frameRect.size.width = [detailsView frame].size.width;
[[scDetailsViewController view] setFrame:frameRect];
[detailsView addSubview:[scDetailsViewController view]];
[scDetailsViewController populateScenarioDetails :[[self scenarioDetailsList] entityAt:[scenarioTableView selectedRow]]];
}
else {
if ([[detailsView subviews] count]>0)
{
[detailsView removeAllSubviews];
}
}
}
Modify your key event code something like this below and let me know if still you face any issue:-
NSInteger keyPress = [[NSApp currentEvent] type];
if (keyPress!=10)
{
}
NSLog(#"mouseclick");
}
else{
NSLog(#"keyboardClick");
}

how to move subviews inside a super view

how to move subviews inside a super view (using osx 10.6) using mouse?
i have programmatically created five NSimageView's as subviews using for loop..
how to select and drag each imageview
In a nutshell, you'd want to make your subviews drag sources, and make your target view a destination. This means implementing something like the NSDraggingSource and NSDraggingDestination protocols.
Take a look at Apple's Drag & Drop documentation:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DragandDrop
finally i got the answer
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (NSView *)hitTest:(NSPoint)aPoint
{
NSEnumerator *subviews = [[self subviews] objectEnumerator] ;
NSView *hitView ;
NSLog(#"hit");
fHitView = nil ;
while (hitView = [subviews nextObject]) {
NSRect frame = [hitView frame] ;
if (NSPointInRect(aPoint,frame))
if ([hitView isKindOfClass:[DraggableView class]] && ![(DraggableView *)hitView dragEnabled]) {
return hitView ;
}
else {
fHitView = hitView ;
fHitPoint = aPoint ;
fFrameWhenHit = [hitView frame] ;
return self ;
}
}
return nil ;
}
- (void)mouseDragged:(NSEvent *)theEvent
{
if (fHitView != nil) {
NSPoint locationInWindow = [theEvent locationInWindow] ;
NSPoint locationInMySelf = [self convertPoint:locationInWindow fromView:[[self window] contentView]] ;
[fHitView setFrame:NSOffsetRect(fFrameWhenHit,locationInMySelf.x - fHitPoint.x, locationInMySelf.y - fHitPoint.y)] ;
[self setNeedsDisplay:YES] ;
}
}
insert into subclass of NSview and change the class name of NScustomView class as subclass name...

Scrolling NSTextView to bottom

I'm making a little server app for OS X and I'm using an NSTextView to log some info about connected clients.
Whenever I need to log something I'm appending the new message to the text of the NSTextView this way:
- (void)logMessage:(NSString *)message
{
if (message) {
self.textView.string = [self.textView.string stringByAppendingFormat:#"%#\n",message];
}
}
After this I'd like the NSTextField (or maybe I should say the NSClipView that contains it) to scroll down to show the last line of its text (obviously it should scroll only if the last line is not visible yet, in fact if then new line is the first line I log it is already on the screen so there is no need to scroll down).
How can I do that programmatically?
Found solution:
- (void)logMessage:(NSString *)message
{
if (message) {
[self appendMessage:message];
}
}
- (void)appendMessage:(NSString *)message
{
NSString *messageWithNewLine = [message stringByAppendingString:#"\n"];
// Smart Scrolling
BOOL scroll = (NSMaxY(self.textView.visibleRect) == NSMaxY(self.textView.bounds));
// Append string to textview
[self.textView.textStorage appendAttributedString:[[NSAttributedString alloc]initWithString:messageWithNewLine]];
if (scroll) // Scroll to end of the textview contents
[self.textView scrollRangeToVisible: NSMakeRange(self.textView.string.length, 0)];
}
As of OS 10.6 it's as simple as nsTextView.scrollToEndOfDocument(self).
Swift 4 + 5
let smartScroll = self.textView.visibleRect.maxY == self.textView.bounds.maxY
self.textView.textStorage?.append("new text")
if smartScroll{
self.textView.scrollToEndOfDocument(self)
}
I've been messing with this for a while, because I couldn't get it to work reliably. I've finally gotten my code working, so I'd like to post it as a reply.
My solution allows you to scroll manually, while output is being added to the view. As soon as you scroll to the absolute bottom of the NSTextView, the automatic scrolling will resume (if enabled, that is).
First a category to #import this only when needed...
FSScrollToBottomExtensions.h:
#interface NSView (FSScrollToBottomExtensions)
- (float)distanceToBottom;
- (BOOL)isAtBottom;
- (void)scrollToBottom;
#end
FSScrollToBottomExtensions.m:
#implementation NSView (FSScrollToBottomExtensions)
- (float)distanceToBottom
{
NSRect visRect;
NSRect boundsRect;
visRect = [self visibleRect];
boundsRect = [self bounds];
return(NSMaxY(visRect) - NSMaxY(boundsRect));
}
// Apple's suggestion did not work for me.
- (BOOL)isAtBottom
{
return([self distanceToBottom] == 0.0);
}
// The scrollToBottom method provided by Apple seems unreliable, so I wrote this one
- (void)scrollToBottom
{
NSPoint pt;
id scrollView;
id clipView;
pt.x = 0;
pt.y = 100000000000.0;
scrollView = [self enclosingScrollView];
clipView = [scrollView contentView];
pt = [clipView constrainScrollPoint:pt];
[clipView scrollToPoint:pt];
[scrollView reflectScrolledClipView:clipView];
}
#end
... create yourself an "OutputView", which is a subclass of NSTextView:
FSOutputView.h:
#interface FSOutputView : NSTextView
{
BOOL scrollToBottomPending;
}
FSOutputView.m:
#implementation FSOutputView
- (id)setup
{
...
return(self);
}
- (id)initWithCoder:(NSCoder *)aCoder
{
return([[super initWithCoder:aCoder] setup]);
}
- (id)initWithFrame:(NSRect)aFrame textContainer:(NSTextContainer *)aTextContainer
{
return([[super initWithFrame:aFrame textContainer:aTextContainer] setup]);
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)awakeFromNib
{
NSNotificationCenter *notificationCenter;
NSView *view;
// viewBoundsDidChange catches scrolling that happens when the caret
// moves, and scrolling caused by pressing the scrollbar arrows.
view = [self superview];
[notificationCenter addObserver:self
selector:#selector(viewBoundsDidChangeNotification:)
name:NSViewBoundsDidChangeNotification object:view];
[view setPostsBoundsChangedNotifications:YES];
// viewFrameDidChange catches scrolling that happens because text
// is inserted or deleted.
// it also catches situations, where window resizing causes changes.
[notificationCenter addObserver:self
selector:#selector(viewFrameDidChangeNotification:)
name:NSViewFrameDidChangeNotification object:self];
[self setPostsFrameChangedNotifications:YES];
}
- (void)handleScrollToBottom
{
if(scrollToBottomPending)
{
scrollToBottomPending = NO;
[self scrollToBottom];
}
}
- (void)viewBoundsDidChangeNotification:(NSNotification *)aNotification
{
[self handleScrollToBottom];
}
- (void)viewFrameDidChangeNotification:(NSNotification *)aNotification
{
[self handleScrollToBottom];
}
- (void)outputAttributedString:(NSAttributedString *)aAttributedString
flags:(int)aFlags
{
NSRange range;
BOOL wasAtBottom;
if(aAttributedString)
{
wasAtBottom = [self isAtBottom];
range = [self selectedRange];
if(aFlags & FSAppendString)
{
range = NSMakeRange([[self textStorage] length], 0);
}
if([self shouldChangeTextInRange:range
replacementString:[aAttributedString string]])
{
[[self textStorage] beginEditing];
[[self textStorage] replaceCharactersInRange:range
withAttributedString:aAttributedString];
[[self textStorage] endEditing];
}
range.location += [aAttributedString length];
range.length = 0;
if(!(aFlags & FSAppendString))
{
[self setSelectedRange:range];
}
if(wasAtBottom || (aFlags & FSForceScroll))
{
scrollToBottomPending = YES;
}
}
}
#end
... You can add a few more convenience methods to this class (I've stripped it down), so that you can output a formatted string.
- (void)outputString:(NSString *)aFormatString arguments:(va_list)aArguments attributeKey:(NSString *)aKey flags:(int)aFlags
{
NSMutableAttributedString *str;
str = [... generate attributed string from parameters ...];
[self outputAttributedString:str flags:aFlags];
}
- (void)outputLineWithFormat:(NSString *)aFormatString, ...
{
va_list args;
va_start(args, aFormatString);
[self outputString:aFormatString arguments:args attributeKey:NULL flags:FSAddNewLine];
va_end(args);
}
I have some customised NSTextView and custom input method so my option was to use:
self.scrollView.contentView.scroll(NSPoint(x: 1, y: self.textView.frame.size.height))

drag and drop: NSView thinks source is an image that it isn't

I have two views.
1. One view has a grid of NSImageViews with images.
2. The other view has a grid of potential places to place the dragged image.
I think I have dragging working. If I click and drag an image from view 1, it works fine, but when I try to place it, the path of the source image is for an image on my desktop that isn't even part of my project. The result of the below code is that carriedData = nil, but I did some testing with NSLog and saw that it is sending the path for an image outside the project that is no my desktop.
Here is some of the D&D protocol methods I have implemented. myDragState is just en enumeration I use for highlighting the grid space that the drag is currently hovering over.
in Init...
[self registerForDraggedTypes:[NSImage imagePasteboardTypes]];
myDragState = dragStateNone;
Then the protocol methods
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
{
myDragState = dragStateOver;
[self setNeedsDisplay];
return NSDragOperationGeneric;
}
else
{
return NSDragOperationNone;
}
}
- (void)draggingExited:(id <NSDraggingInfo>)sender
{
//turn off focus ring
myDragState = dragStateExited;
[self setNeedsDisplay];
}
- (void)draggingEnded:(id <NSDraggingInfo>)sender { }
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
{
return YES;
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
NSPasteboard *paste = [sender draggingPasteboard];
//gets the dragging-specific pasteboard from the sender
NSArray *types = [NSArray arrayWithObjects:NSTIFFPboardType, nil];
//a list of types that we can accept
NSString *desiredType = [paste availableTypeFromArray:types];
NSData *carriedData = [paste dataForType:desiredType];
if (nil == carriedData)
{
//the operation failed for some reason
NSRunAlertPanel(#"Paste Error", #"Sorry, but the past operation failed", nil, nil, nil);
myDragState = dragStateNone;
[self setNeedsDisplay];
return NO;
}
else
{
//the pasteboard was able to give us some meaningful data
if ([desiredType isEqualToString:NSTIFFPboardType])
{
NSLog(#"TIFF");
//we have TIFF bitmap data in the NSData object
NSImage *newImage = [[NSImage alloc] initWithData:carriedData];
[self setImage:newImage];
myDragState = dragStateSet;
}
else
{
//this can't happen
//NSAssert(NO, #"This can't happen");
NSLog(#"Other type");
myDragState = dragStateNone;
[self setNeedsDisplay];
return NO;
}
}
//redraw us with the new image
return YES;
}
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
{
//re-draw the view with our new data
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
NSRect ourBounds = [self bounds];
if (myDragState == dragStateSet)
{
NSImage *image = [self image];
[super drawRect:dirtyRect];
[image compositeToPoint:(ourBounds.origin) operation:NSCompositeSourceOver];
}
else if (myDragState == dragStateOver)
{
[[NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:0.4f] set];
[NSBezierPath fillRect:ourBounds];
}
else {
//draw nothing
}
}
Edit: So I figured this out. The problem was actually with the source. I wasn't copying it to the PBoard properly. My code for that is:
NSPasteboard *zPasteBoard = [NSPasteboard pasteboardWithName:NSDragPboard];
[zPasteBoard declareTypes:[NSArray arrayWithObject:NSTIFFPboardType] owner:self];
[zPasteBoard setData:[tileImageView.image TIFFRepresentation] forType:NSTIFFPboardType];
Now I am getting some weird effect when the images show though. As the images are placed in the destination imageview, they are being resized, but the larger draggable version is also still showing up. So, I am getting two images on the image view.

Resources