I dragged a texture atlas into my project. The pictures are named correctly ("heliani_1-9")
The animation is running smooth, except for 3 frames, which are displayed as big red cross on a white ground. (See screenshot enclosed)
What is wrong with my code?
Cheers
#import "MRMPlayer.h"
#implementation MRMPlayer
-(instancetype)init
{
self = [super init];
{
[self setupAnimations];
[self runAction:[SKAction repeatActionForever:[SKAction animateWithTextures:self.runFrames timePerFrame:0.5 resize:YES restore:NO]] withKey:#"heli"];
self.name = playerName;
}
return self;
}
-(void) setupAnimations{
self.runFrames = [[NSMutableArray alloc]init];
SKTextureAtlas *heliAtlas = [SKTextureAtlas atlasNamed:#"heli"];
for (int i = 0; i < [heliAtlas.textureNames count]; i++) {
NSString *tempName = [NSString stringWithFormat:#"heliani_%d",i];
SKTexture *tempTexture = [heliAtlas textureNamed:tempName];
if(tempTexture) {
[self.runFrames addObject:tempTexture];
}
}
}
#end
Go to the product menu, and you will see the Clean option.
Now, hold down the option button on your keyboard and the text should change to Clean build folder...
Choose that option, and it will additionally delete the derivative data folder that caches alot of things including the texture atlas, and I've found to cause problems like you have described. If you rename files in the atlas, thats typically when I have experienced this issue myself.
I don't like that this option is something you kind of have to work for, would be nice to have this second option there WITHOUT having to hold down the option key.
If this doesn't solve the problem, you truly have a naming problem imo.
Note
You can also delete the derivative data folders from the Organizer window.
Related
My SpriteKit game has three scenes for now; Menu.m, LevelSelect.m and Level.m. When I start the app, the memory usage is 35MB. Upon transitioning from the main menu to the level selection scene it pretty much stays around 35MB. Moving from the level select scene to the level itself it shoots up to around 150MB. Granted, there are more sprites and classes involved in creating the level. However, when I reload the level via an overlay menu that I have as a sprite the memory usage continues to rise by around 2MB for each reload. Could it be a retain cycle issue? The same is true if I switch back to the level select scene and then re-enter the level itself, the memory continues to climb and climb. I am concerned about my implementation of my state machine. I'll post some skeletal code below:
LevelScene.m
-(void)didMoveToView:(SKView *)view {
...
[self initGameStateMachine];
...
}
...
//other methods for setting up the level present
...
-(void) initGameStateMachine {
LevelSceneActiveState *levelSceneActiveState = [[LevelSceneActiveState alloc] initLevelScene:self];
LevelSceneConfigureState *levelSceneConfigureState = [[LevelSceneConfigureState alloc] initLevelScene:self];
LevelSceneFailState *levelSceneFailState = [[LevelSceneFailState alloc] initLevelScene:self];
LevelSceneSuccessState *levelSceneSuccessState = [[LevelSceneSuccessState alloc] initLevelScene:self];
NSMutableDictionary *states = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
levelSceneActiveState, #"LevelSceneActiveState",
levelSceneConfigureState, #"LevelSceneConfigureState",
levelSceneFailState, #"LevelSceneFailState",
levelSceneSuccessState, #"LevelSceneSuccessState",
nil];
_gameStateMachine = [[StateMachine alloc] initWithStates:states];
[_gameStateMachine enterState:levelSceneActiveState];
}
-(void)update:(CFTimeInterval)currentTime {
//update the on screen keyboard with notes that are being played
[_gameStateMachine updateWithDeltaTime:[NSNumber numberWithDouble:currentTime]];
}
-(void) willMoveFromView:(SKView *)view {
[self removeAllChildren];
}
StateMachine.m
#import "StateMachine.h"
#import "GameState.h"
#interface StateMachine()
#property NSMutableDictionary *states;
#end
#implementation StateMachine
-(instancetype)initWithStates:(NSMutableDictionary*)states {
if (self = [super init]) {
for (id key in [states allValues]) {
if (![key conformsToProtocol:#protocol(GameState)]) {
NSLog(#"%# does not conform to #protocol(GameState)", key);
return nil;
}
}
_states = states;
}
return self;
}
//this method will be used to start the state machine process
-(bool)enterState:(id)nextState {
if (!_currentState) {
_currentState = [_states objectForKey:[nextState className]];
[[NSNotificationCenter defaultCenter] addObserver:_currentState selector:#selector(keyPressed:) name:#"KeyPressedNotificationKey" object:nil];
return YES;
}
else if ([_currentState isValidNextState:nextState]) {
[_currentState performSelector:#selector(willLeaveState)];
_currentState = [_states objectForKey:[nextState className]];
[[NSNotificationCenter defaultCenter] addObserver:_currentState selector:#selector(keyPressed:) name:#"KeyPressedNotificationKey" object:nil];
return YES;
}
return NO;
}
-(void)updateWithDeltaTime:(NSNumber*)currentTime {
[_currentState performSelector:#selector(updateWithDeltaTime:) withObject:currentTime];
}
#end
LevelSceneActiveState //this is one of the 4 states
#import "LevelSceneActiveState.h"
#import "LevelSceneConfigureState.h"
#import "LevelSceneSuccessState.h"
#import "LevelSceneFailState.h"
#import "StateMachine.h"
#import "LevelScene.h"
#import "SSBitmapFontLabelNode.h"
#interface LevelSceneActiveState()
#property LevelScene *levelScene;
#end
#implementation LevelSceneActiveState
-(instancetype)initLevelScene:(LevelScene *)levelScene {
if (self = [super init])
_levelScene = levelScene;
return self;
}
-(void)updateWithDeltaTime:(NSNumber*)currentTime {
//game variables created here
....
//state machine needs to be set here...if set in init, it does not have a value in the LevelScene yet
if (_gameStateMachine == nil)
_gameStateMachine = _levelScene.gameStateMachine;
//game logic performed here
...
//check for timer finishing
if (!_levelScene.timer.isValid) {
//success
if (_levelScene.score >= 7) {
[_gameStateMachine enterState:LevelSceneSuccessState.self];
}
else { //failure
[_gameStateMachine enterState:LevelSceneFailState.self];
}
}
}
//another class is used to trigger notifications of key presses
-(void) keyPressed:(NSNotification*)notification {
NSNumber *keyCodeObject = notification.userInfo[#"keyCode"];
NSInteger keyCode = keyCodeObject.integerValue;
if (keyCode == 53)
[self escapePressed];
}
-(void) escapePressed {
[_gameStateMachine enterState:LevelSceneConfigureState.self];
[_levelScene childNodeWithName:#"timer"].paused = YES;
}
-(bool)isValidNextState:(id)nextState {
if ([[nextState className] isEqualToString:#"LevelSceneConfigureState"])
return YES;
...
return NO;
}
//this makes sure that we're not notifying an object that may not exist
-(void) willLeaveState {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"KeyPressedNotificationKey"
object:nil];
}
#end
When switching between states I do not want the LevelScene scene to go away. I understand that this is hard to diagnose especially when you're not sitting in front of the full project. What can I do to self-diagnose this myself? Any helpful tips/tricks would be great.
[UPDATE] I tried the Product->Profile->Instruments->Allocation thing, but I have no idea what to do with it. The memory is indicating that it continues to rise though.
Instrument Tutorial Article
For people that are as clueless as me when it comes to using Xcode's Instruments, here is an amazing article from Ray Wenderlich that really dumbs it down!
I found so many issues in my project that I didn't even think were issues because of that article. I'm not surprised that no one posted an answer regarding this question because when you have these types of issues they are very personal to the project that you are working on and quite hard to debug.
My Issue + Solution
My issue is a common one. I was loading a set of resources, in this case a .sf2 (sound font) file, over and over and over again when my scene reloaded. I honestly thought I had gotten rid of that issue with all of my sprites.
Here's how I found it using Instruments:
In Xcode go to Product->Profile then select Allocations
This window should pop up:
Click on the red circle button at the top left (this will start your app)
Perform the operations in your app that seem to cause issues then press Mark Generation
Repeat the operations that cause the issues and continue to press Mark Generation
Note: Mark Generation takes a snapshot of the app at that time so that you can see the changes in memory
Stop the app from running and then dive into one of the generations (I chose Generation C, because that's when the differences in memory usage became constant)
My sampler (an AVAudioUnitSampler object) shown as SamplerState is allocating a bunch of memory
I clicked on the small arrow to the right of SamplerState and it brought me into this view (shows a ton of items that are allocated memory)
Clicking on one of them plus clicking on the extended details button will allow you to see the stack trace for this item
I double clicked on the method that I thought would be the issue
After double clicking on the method, it brings up another view with your code in it along with percentages of what lines of code allocate the most memory (extremely helpful!)
Note: The culprit, in my case, was allocating roughly 1.6MB every time I reloaded the level! Ignore the commented out code, I took the screen shot of a saved session after I fixed the issue.
After fixing my code there is no longer any major memory allocations..although I still have some things to clean up! Only 33KB between level reloads, which is much better!
I have a NSTextView with setImportGraphics(true), I can drag images there, they show up in the interface but I have no idea how to programmatically get the image (and store it) once it's been dragged.
If I call myNSTextView.string all I get is the text around the image, but the image seems non-existent.
Do I have to implement some methods regarding drag and drop to manage this case?
I have no idea how to programmatically get the image (and store it) once it's been dragged.
Dropped images are added to the NSTextStorage as NSTextAttachment. So, in order to access the dropped images you should iterate over the content of the textStorage and check for attachments that conforms to image files.
Do I have to implement some methods regarding drag and drop to manage this case
You certainly can handle the dropped files by extending NSTextView and overwriting the - (void)performDragOperation:(id<NSDraggingOperation>)sender method, if you want to go this way, I recommend you to read the Drag and Drop Programming Topics document from Apple.
Since I'm no fan of subclassing, my answer to this problem uses a Category of NSAttributedString to return a NSArray of attached images. Which can be solved with the code below:
#import "NSAttributedString+AttachedImages.h"
#implementation NSAttributedString (AttachedImages)
- (NSArray *)images
{
NSMutableArray *images = [NSMutableArray array];
NSRange effectiveRange = NSMakeRange(0, 0);
NSTextAttachment *attachment;
CFStringRef extension;
CFStringRef fileUTI;
while (NSMaxRange(effectiveRange) < self.length) {
attachment = [self attribute:NSAttachmentAttributeName atIndex:NSMaxRange(effectiveRange) effectiveRange:&effectiveRange];
if (attachment) {
extension = (__bridge CFStringRef) attachment.fileWrapper.preferredFilename.pathExtension;
fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL);
if (UTTypeConformsTo(fileUTI, kUTTypeImage)) {
NSImage *theImage = [[NSImage alloc] initWithData:attachment.fileWrapper.regularFileContents];
[theImage setName:attachment.fileWrapper.preferredFilename];
[images addObject:theImage];
}
}
}
return images.copy;
}
#end
If you use GIT, you can clone the code from my Github repository.
I hope it helps
So I have been at it all day to no luck and it has been needless to say quite frustrating, I have looked up many examples and downloadable categories which all tout being able to crop images flawlessly. Which they do, However the minute i try to do it from an image genrated via AVCaptureSession it does not work as well. I consulted both these sources
http://codefuel.wordpress.com/2011/04/22/image-cropping-from-a-uiscrollview/
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
and the project from the first link seems to work directly as advertised but as soon as i hack it to do the same magic on an av capture image...nope...
does anyone have insight into this? Also here is my code for reference.
- (IBAction)TakePhotoPressed:(id)sender
{
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections)
{
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
//NSLog(#"about to request a capture from: %#", stillImageOutput);
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
CFDictionaryRef exifAttachments = CMGetAttachment( imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments)
{
// Do something with the attachments.
//NSLog(#"attachements: %#", exifAttachments);
}
else
NSLog(#"no attachments");
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
NSLog(#"%f",image.size.width);
NSLog(#"%f",image.size.height);
float scale = 1.0f/_scrollView.zoomScale;
CGRect visibleRect;
visibleRect.origin.x = _scrollView.contentOffset.x * scale;
visibleRect.origin.y = _scrollView.contentOffset.x * scale;
visibleRect.size.width = _scrollView.bounds.size.width * scale;
visibleRect.size.height = _scrollView.bounds.size.height * scale;
UIImage* cropped = [self cropImage:image withRect:visibleRect];
[croppedImage setImage:cropped];
[image release];
}
];
[croppedImage setHidden:NO];
}
cropImage function used above.
-(UIImage*)cropImage :(UIImage*)originalImage withRect :(CGRect) rect
{
CGRect transformedRect=rect;
if(originalImage.imageOrientation==UIImageOrientationRight)
{
transformedRect.origin.x = rect.origin.y;
transformedRect.origin.y = originalImage.size.width-(rect.origin.x+rect.size.width);
transformedRect.size.width = rect.size.height;
transformedRect.size.height = rect.size.width;
}
CGImageRef cr = CGImageCreateWithImageInRect(originalImage.CGImage, transformedRect);
UIImage* cropped = [UIImage imageWithCGImage:cr scale:originalImage.scale orientation:originalImage.imageOrientation];
[croppedImage setFrame:CGRectMake(croppedImage.frame.origin.x,
croppedImage.frame.origin.y,
cropped.size.width,
cropped.size.height)];
CGImageRelease(cr);
return cropped;
}
I am also tempted for verbosity and arming whomever might help me in my plight with as much information as possible to post my init of my scrollView and avcapture session. However That may be a bit too much so if you want to see it just ask.
Now as for results of what the code actually does?..
What it looks like before i take the picture
And After...
EDIT:
Well I have a few views now and no comment's so either no one has figured it out or it's so simple they thought i would have figured it out again...In any case i have not made any progress. So for anyone interested here is a small sample app with the code all set up and you can see what i am doing
https://docs.google.com/open?id=0Bxr4V3a9QFM_NnoxMkhzZTVNVEE
It seems that this little conundrum did not only have me stumped as after nearly a week,but a scant few of whoever viewed my question had no suggestions either. I must say for this particular problem i could not get it to work in this way, I pondered and tinkered and mused for a while to no avail. Until i did this
[self HideElements];
UIGraphicsBeginImageContext(chosenPhotoView.frame.size);
[chosenPhotoView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self ShowElements];
And that's it, less code and it worked pretty much instantly. So instead of trying to crop an image via the scrollview I take a screenshot of the screen at that time then crop the image using the scrollviews frame variables. And the hide/show element functions hide any overlapping elements on the picture i want.
The above says it all- I have a UITextField set to secure, but want to give users the option to make it not secure (so they can see for sure what they typed if they are in a private area). However, suppose they hit the toggle by mistake, and want to change it back to secure mode? That does not work. I've tried everything - using -1 instead of YES, deleting the text and then putting it back. I'm at a total loss on other ideas. [Entered rdar://9781908]
EDIT: I believe this issue is fixed as of iOS5.
It must be input-focus issue: when focused, UITextField can change only ON->OFF.
Try next trick to switch OFF->ON:
textField.enabled = NO;
textField.secureTextEntry = YES;
textField.enabled = YES;
[textField becomeFirstResponder];
EDIT (dhoerl): In iOS 9.3, I found this works but there is a problem. If you enter say 3 characters in plain view, then switch to secure text, then type a character, the 3 pre-existing characters disappear. I tried all kinds of trick to clear, then reset the text without success. I finally tried just playing with the control by cutting and pasting - pasting into the newly-switched-to-secure-mode worked great. So I emulated it in code, and now it all works fine - no need to play with the responder either. Here is what I finally ended up with:
if textview should go into secure mode and the textfield is not empty {
textField.text = ""
textField.secureTextEntry = true
UIPasteboard.generalPasteboard().string = password
textField.paste(self)
UIPasteboard.generalPasteboard().string = ""
}
I'd prefer to not have to do this paste, but if you want it seamless this is the only way I can find to do it.
Mike R's solution is nice, but I prefer this approach:
BOOL wasFirstResponder;
if ((wasFirstResponder = [passwordField isFirstResponder])) {
[passwordField resignFirstResponder];
}
// In this example, there is a "show password" toggle
[passwordField setSecureTextEntry:![passwordField isSecureTextEntry]];
if (wasFirstResponder) {
[passwordField becomeFirstResponder];
}
That way you only becomeFirstResponder again when necessary.
I entered rdar against this problem, but did find a work around. Essentially you have to programmatically replace the "stuck" control with a new one. The easiest thing to do is to archive the existing control in viewDidLoad then unarchive as needed:
// do in viewDidLoad
self.passwordMemberArchive = [NSMutableData data];
NSKeyedArchiver *ka = [[NSKeyedArchiver alloc] initForWritingWithMutableData:passwordMemberArchive];
[ka encodeObject:password];
[ka finishEncoding];
[ka release];
// In the action method when you get the UISwitch action message ---
// when your switch changes state
if(isOn) {
NSString *text = [NSString stringWithString:password.text];
NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
UITextField *tf = [kua decodeObject];
[kua finishDecoding];
[kua release];
tf.inputAccessoryView = textField.inputAccessoryView;
tf.frame = textField.frame;
BOOL isFirstResponder = [textField isFirstResponder];
[scrollView insertSubview:tf aboveSubview:textField];
if(isFirstResponder) {
[tf becomeFirstResponder];
}
[textField removeFromSuperview];
self.password = tf;
if([text length]) {
if(isFirstResponder) {
// http://stackoverflow.com/questions/1317929/insert-string-at-cursor-position-of-uitextfield
// Get a reference to the system pasteboard
UIPasteboard* lPasteBoard = [UIPasteboard generalPasteboard];
// Save the current pasteboard contents so we can restore them later
NSArray* lPasteBoardItems = [lPasteBoard.items copy];
// Update the system pasteboard with my string
lPasteBoard.string = text;
// Paste the pasteboard contents at current cursor location
[tf paste:self];
// Restore original pasteboard contents
lPasteBoard.items = lPasteBoardItems;
[lPasteBoardItems release];
} else {
tf.text = text;
}
}
} else {
textField.secureTextEntry = NO;
}
With iOS, you should never try to "hack" stuff, if the behavior you want is not provided by the framework, change your mind !
First its easier ^^, second the user will not be responsive to this, then you never know if the next iOS update will break it or not, so it can be dangerous for your application.
"You want the user sees the password he is taping on a secured textfield", you can display a UILabel in the bottom instead ? Or a confirmation Alert box with the clear password ?
Swift version of Sandy's solution.
if #available(iOS 9.2, *) {
passwordTextField.secureTextEntry = !passwordTextField.secureTextEntry
}
else {
let wasFirstResponder = passwordTextField.isFirstResponder()
if wasFirstResponder {
passwordTextField.resignFirstResponder()
}
passwordTextField.secureTextEntry = !passwordTextField.secureTextEntry
if wasFirstResponder {
passwordTextField.becomeFirstResponder()
}
}
I'm trying to clean up the UI on my application (built for 10.5) and one thing that's rather annoying is that when I swap to the library I reload the contents and the nscollectionview that displays the items fades the old content out before fading the new ones in. I don't mind the fade in/out of old/new items, but the way i've programmed the view swapping should make the library reload happen before the the nsview that contains the nscollectionview is inserted into the main window.
Anyone had this sort of problem before?
Here's an abridged version of how the swap between different parts of the application to the library goes:
In the main controller:
-(void)setMainPaneToLibrary {
if(viewState == VIEW_STATE_LIBRARY)
return;
[libraryController refreshDevices:self];
[libraryController refreshLibraryContents];
[menuController setSelectionToIndex:0];
viewState = VIEW_STATE_LIBRARY;
[self removeSubview]; //clear the area
[mainPane addSubview:[libraryController view]];
}
In the library controller:
-(void)refreshLibraryContents {
[self loadLibraryContents:[rootDir stringByAppendingPathComponent:currentDir]];
}
-(void)loadLibraryContents:(NSString *)folderToLoad {
int i;
int currentSelection = [libraryArrayController selectionIndex]; //used to preserve selection and smooth folder changing
[libraryArrayController setContent:nil];
//expand the path to load
folderToLoad = [folderToLoad stringByExpandingTildeInPath];
NSFileManager * fileManager = [NSFileManager defaultManager];
//load the files in the folder
NSArray * contents = [fileManager directoryContentsAtPath:folderToLoad];
//create two seperate arrays for actual files and folders - allows us to add them in order
NSMutableArray * directories = [[NSMutableArray alloc] init];
NSMutableArray * musicFiles = [[NSMutableArray alloc] init];
//... a load of stuff filling the file arrays ...
for(i=0;i<[directories count];i++) {
[libraryArrayController addObject:[directories objectAtIndex:i]];
}
for(i=0;i<[musicFiles count];i++) {
[libraryArrayController addObject:[musicFiles objectAtIndex:i]];
}
if(currentSelection >= 0) {
[libraryArrayController setSelectionIndex:currentSelection];
}
[directories release];
[musicFiles release];
}
The issue is not that the collection view hasn't finished drawing, it's that it animates the adding and removal of the subviews. It may be possible to create a transaction that disables layer actions while you're reloading the collection view's content. Listing 2 (temporarily disabling a layer's actions) shows how to do this. I haven't tested this, so I don't know if this will do what you want, but this is where I'd start.