I would like to access a UIViewAnimationTransition by its corresponding number (please see image, number 1.). Also, what does the second number correspond to (image, number 2) and is it relevant to accessing the first. Something like this below, where option is defined quickly and then e.g. fed into an animation block.
UIViewAnimationOptions option = 3; // UIViewAnimationOptionTransitionCurlUp
UIImage * toImage = [backgroundImages objectAtIndex:backgroundImageCount];
[UIView transitionWithView:backgroundView
duration:transitionDuration
options:option
animations:^{
[backgroundView setImage:toImage];
} completion:nil];
Those are bit shifted values. For example 1<<20 is equal to 1048576. You can calculate them in Spotlight if you need them
That said, you shouldn't reference them by their numbers.
Related
I would like to read the list of colors that are shown on the bottom of the NSColorPanel (see image below). Is there a way to do this?
For undocumented access (this may not work within a sandbox and will get your app rejected by Apple if you plan to distribute through the App Store):
NSArray *libraries = [[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSAllDomainsMask];
NSURL *url = [[libraries objectAtIndex:0] URLByAppendingPathComponent:#"Colors/NSColorPanelSwatches.plist"];
NSData *fileData = [NSData dataWithContentsOfURL:url];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:fileData];
NSArray *colors = [unarchiver decodeObjectForKey:#"NSSwatchColorArray"];
The colors array will then contain NSColor objects of the colour panel.
This works as far back as OS X 10.6. It may work on earlier versions also but you'll need to obtain the filename differently (since URLsForDirectory:inDomains: was introduced in 10.6). Inside the NSColorPanelSwatches.plist file is an internal version number which is set to 6 for 10.6 right through to 10.10. It could change in the future, but you could be more-or-less safe by doing:
if ([unarchiver decodeIntForKey:#"NSSwatchFileVersion"] == 6)
{
NSArray *colors = [unarchiver objectForKey:#"NSSwatchColorArray"];
// do something with colors
}
else
{
NSLog(#"System unsupported");
}
If you're interested in where the positions of the colours are, you can decode an NSIndexSet from the unarchiver using the NSSwatchColorIndexes key, and use that index set in conjunction with the number of rows and columns that you can determine by decoding integers with the keys NSSwatchLayoutNumRows and NSSwatchLayoutNumColumns. The nth index in the index set refers to the location of the nth colour in the array and the indexes increase downwards. For example, the first “colour box” in the panel is index 0, and the box below it is index 1. The box to the right of “index 0” is actually index 10 (or whatever number you decoded from NSSwatchLayoutNumRows).
So if you have a colour in the first box, and another colour in the box to the right, you'll have two NSColor objects in the colors array, and the NSIndexSet will contain two indexes, 0 and 10.
Unfortunately there are no public APIs for accessing system-wide user-provided colors from the swatches. This is a very old issue. NSColorPanel is long overdue for an overhaul from Apple...
Ok so I've recently decided to try to teach myself Objective-C (I'm interested in iPhone development), however I've never used C or any of its derivatives before, and as such am running into some problems.
I decided to start out by writing a very basic card application that creates a deck of cards, shuffles the deck, and then displays the cards on the screen using UIButtons, however I'm having a problem with my shuffling algorithm. Every time it gets called I get an EXC_BAD_ACCESS error, which I know means there's something desperately wrong with my code, but I just can't figure out what it is.
- (void) randomize {
NSMutableArray *tmpDeck = [[NSMutableArray alloc] init];
for(Card *tmp in _cards) {
BOOL didInsert = NO;
while(!didInsert) {
NSUInteger random = arc4random_uniform(54);
if([[tmpDeck objectAtIndex:random] isEqual:nil]) {
[tmpDeck insertObject:tmp atIndex:random];
didInsert = YES;
}
}
}
_cards = tmpDeck;
_hasBeenRandomized = YES;
}
_cards is a pointer to an NSMutableArray containing the unshuffled deck of card objects, and _hasBeenRandomized is a boolean (obviously) that keeps track of whether or not the deck has been randomized.
I've tried to use the debugger to work out what exactly is going on here, but I can't even step into the method without my program crashing. This leads me to believe that the problem has to come from the very first line, but it's just a straightforward creation of an NSMutableArray, so I don't know how it could be that. This method is being called from within viewDidLoad. This is the entirety of the viewDidLoad method currently.
- (void)viewDidLoad
{
[super viewDidLoad];
_deck = [[Deck alloc] init];
[_deck randomize];
}
Any and all help will be appreciated. Sorry if the answer is dead obvious.
This is because you are trying to insert into an index that doesn't exist yet. You need to initialize the array with as many places in the array as you need for your cards. Either that or use a NSMutableDictionary and just insert the object with the index being the key.
To add another note, calling initWithCapacity on the array wouldn't solve this for you either since this just gives a "hint" at the size. You need the count property of the array to actually be at least as large as the index you are trying to insert. If you wanted to do an array, then you would first need to populate something in each index first. You could define this in the new array literal format or use a for loop that loops the number of times you need (your max index) and insert a dummy object in it's place.
for (int i=0; i< _cards.count; ++i)
{
[tmpDeck insertObject:#"dummy" atIndex:i];
}
Then instead of checking for 'nil' before you replace, you check if it is equal to the dummy object you inserted. This would give you an array that you can insert into any of these indexes. I personally would still probably store them in an NSMutableDictionary. But if you need it in an array for some other purpose then this is a way to do it.
You also will need to be sure to replace the object instead of inserting, otherwise you will just keep adding indexes.
[tmpDeck replaceObjectAtIndex:random withObject:tmp];
If you still get the same error, set a breakpoint in your debugger and check what the random number is and what the count of your array is. If your random number is ever greater than your array count, then you will get this error.
I have an array of UIImageViews:
NSMutableArray *imagesArray = [[NSMutableArray alloc] init];
[imagesArray addObject:smallBox];
[imagesArray addObject:medBox];
[imagesArray addObject:largeBox];
[imagesArray addObject:xlBox];
These boxes are being moved on the screen. I would like the array sorted so that whichever box is on the left side of the screen (x position) is in the first position of the array etc. I think I would be able to figure it out if I knew how to access the properties of the images while in the array. I tried using code like imagesArray[0].frame.origin.x; but that does not work. Any help would be appreciated.
I'd do it like this:
NSSortDescriptor *desc = [NSSortDescriptor sortDescriptorWithKey:#"frame.origin.x" ascending:YES];
[imagesArray sortUsingDescriptors:#[desc]];
That's assuming that all the objects in the array are views -- make sure that smallBox, medBox, etc. are all instances of UIImageView and not UIImage. Also, note that in order for the image views' x coordinate to be sort the way you want them to, they should all be subviews of the same superview. If they're not, you're not comparing apples to apples. If the views are not all subviews of the same view, you'll need to write a comparator that first converts the view's frame to screen coordinates before comparing.
Your expression (imagesArray[0].frame.origin.x) should give you the x coordinate of the view at imagesArray[0], and if it doesn't it'd be helpful to know how/why it's failing.
I just finished debugging the hell out of my project after I found it was working fine on the simulator but would crash when tested on a device with an EXC BAD ACCESS error at the #autoreleasepool using ARC.
I finally narrowed the problem down to where I had a custom init method for a custom class I created that accepted two structs as parameters. The structs were based off of the same definition, containing only 3 GLfloats to represent x, y and z data for use with positioning and rotation.
When I modified the custom init method to instead accept 6 GLfloats instead of two structs each containing 3 GLfloats, and then had the init method assign these GLfloats to the two appropriate instance variable structs of the class instead of assigning the previously passed structs directly to the instance variable structs, it all worked fine without error.
For a bit of clarification:
I had a struct defined like so:
struct xyz{
GLfloat x;
GLfloat y;
GLfloat z;
};
I then had two ivars of a custom class (lets call it foo) called position and rotation, both based off of this struct.
I then had a custom init method simplified to this:
-(id) initWithPosition: (struct xyz) setPosition rotation: (struct xyz) setRotation
{
self = [super init];
if(self) {
position = setPosition;
rotation = setRotation;
}
return self;
}
Which I called using something similar to:
struct xyz position;
struct xyz rotation;
// Fill position / rotation with data here....
foo *bar = [[foo alloc] initWithPosition: position rotation: rotation];
Which resulted in the EXC BAD ACCESS. So I changed the init method to this:
-(id) initWithPositionX: (GLfloat) xp Y: (GLfloat) yp Z: (GLfloat) zp RotationX: (GLfloat) xr Y: (GLfloat) yr Z: (GLfloat) zr
{
self = [super init];
if(self) {
position.x = xp;
position.y = yp;
position.z = zp;
rotation.x = xr;
rotation.y = yr;
rotation.z = zr;
}
return self;
}
And... all is well.
I mean, Im glad I "fixed" it, but Im not sure why I fixed it. Ive read that ARC has issues with using structs of objects, but my structs are only comprised of simple GLfloats, which aren't objects... right? Id rather know why this is working before I move on, and hopefully help some others out there experiencing the same problem.
Thanks,
- Adam Eisfeld
EDIT: Adding Source
Try as I might I cant seem to duplicate the issue in a test project, so there must be something wrong with my code as others have suggested and I haven't fixed the problem at all, only masked it until later, which is what I was afraid of.
So if anyone can take a look at this code to point out what might be going wrong here (if the issue is indeed in the init method, which maybe its not), Id greatly appreciate it. Its rather large so I understand if nobody is interested, but Ive commented it and Ill explain whats going on in further detail after the code snippet:
-(id) initWithName: (NSString*) setName fromFile: (NSString*) file
{
self = [super init];
if(self) {
//Initialize ivars
name = setName;
joints = [[NSMutableArray alloc] init];
animations = [[NSMutableArray alloc] init];
animationCount = 0;
frameCount = 0;
//Init file objects for reading
NSError *fileError;
NSStringEncoding *encoding;
//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:#"dat"] usedEncoding:encoding error:&fileError];
//Create a new NGLMaterial to apply to all of the loaded models from the file
NGLMaterial *material = [[NGLMaterial alloc] init];
material = [NGLMaterial material];
material.diffuseMap = [NGLTexture texture2DWithFile:#"resources/models/diffuse.bmp"];
//Check for nil file data
if(fileData == nil)
{
NSLog(#"Error reading mesh file");
}
else
{
//Separate the NSString of the file's contents into an array, one line per indice
NSMutableArray *fileLines = [[NSMutableArray alloc] initWithArray:[fileData componentsSeparatedByString:#"\n"] copyItems: YES];
//Create a pseudo counter variable
int i = -1;
//Allocate a nul NSString for looping
NSString *line = [[NSString alloc] initWithFormat:#""];
//Loop through each of the lines in the fileLines array, parsing the line's data
for (line in fileLines) {
//Increase the pseudo counter variable to determine what line we're currently on
i++;
if (i == 0) {
//The first line of the file refers to the number of animations for the player
animationCount = [line intValue];
}else
{
if (i == [fileLines count]-2) {
//The last line of the file refers to the frame count for the player
frameCount = [line intValue];
}else
{
//The lines inbetween the first and last contain the names of the .obj files to load
//Obtain the current .obj path by combining the name of the model with it's path
NSString *objPath = [[NSString alloc] initWithFormat:#"resources/models/%#.obj", line];
//Instantiate a new NGLMesh with the objPath NSString
NGLMesh *newMesh = [[NGLMesh alloc] initWithOBJFile:objPath];
//Apply various settings to the mesh such as material
newMesh.material = material;
newMesh.rotationOrder = NGLRotationOrderZYX;
//Compile the changes to the mesh
[newMesh compileCoreMesh];
//Add the mesh to this player's joints array
[joints addObject:newMesh];
//Read the animation data for this joint from it's associated file
NSLog(#"Reading animation data for: %#", line);
//The associated animation file for this model is found at (model's name).anim
NSString *animData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:line ofType:#"anim"] usedEncoding:encoding error:&fileError];
//Check for nil animation data
if(animData == nil)
{
NSLog(#"Error reading animation file");
}
else
{
//Construct temporary position and rotation structs to store the read xyz data from each frame of animation
struct xyz position;
struct xyz rotation;
//Create a new scanner to scan the current animation file for it's xyz position / rotation data per frame
NSScanner *scanner = [[NSScanner alloc] initWithString:animData];
while([scanner isAtEnd] == NO)
{
//Extract position data
[scanner scanFloat:&position.x];
[scanner scanFloat:&position.y];
[scanner scanFloat:&position.z];
//Extract rotation data
[scanner scanFloat:&rotation.x];
[scanner scanFloat:&rotation.y];
[scanner scanFloat:&rotation.z];
//OLD CODE NOT WORKING:
//AEFrame *frame = [[AEFrame alloc] initWithPosition: position rotation: rotation];
//Initialize new frame instance using new working init method
AEFrame *frame = [[AEFrame alloc] initWithPositionX:position.x Y:position.y Z:position.z RotationX:rotation.x Y:rotation.y Z:rotation.z];
//Add the created frame instace to the player's animations array
[animations addObject:frame];
}
}
}
}
}
}
}
return self;
}
Alright so essentially I am writing the code that handles animating the 3D joints of a given player in my engine. I wrote a script for Maya in MEL that allows you to select a series of animated models in the viewport (in this case the joints of a robot character IE the upper and lower arms, the head, the torso, etc) and then run the script which will go through each selected model and export a .anim file. This .anim file contains the xyz position and rotation of the joint the file refers to at each frame of it's animation, so that it is structured like this:
Frame 1 X Position
Frame 1 Y Position
Frame 1 Z Position
Frame 1 X Rotation
Frame 1 Y Rotation
Frame 1 Z Rotation
Frame 2 X Position
Frame 2 Y Position
etc...
However this animation file consists purely of the floats refering to the xyz positions and rotations, there is no actual text labeling each line as shown above, thats only for reference.
When the exporting of .anim files is done for each selected joint of the animated character, the script exports one final file with a .dat extension. This file contains the amount of animations exported (this value is set in the script, for example you might be exporting 3 animations to a .anim file such as Run, Walk and Idle), it then contains a list of names. These names refer to both the names of the .obj files to load into the 3D engine and also the corresponding .anim files to load for the loaded .obj files.
With that explained, Ill describe how Im handling this in my project:
The user instantiates a new "AEPlayer" class using the above method -initWithName: File:
This method first opens up the .dat file exported from maya to find the names of the files to load in for the player's joints.
For every joint name found in the .dat file, the system proceeds to then instantiate a new NGLMesh instance using the joint's name with a ".obj" at the end of it, and then open up the current joint's .anim file. The system also add's the instantiated NGLMesh to the player's joints array.
For each joint's .anim file, it reads through the file using an NSScanner. It scans in 6 lines at a time (the xyz position and xyz rotation of each frame). After scanning in one frame's data, it instantiates a class called AEFrame and sets the frame's position data to the loaded position data and rotation data to the loaded rotation data. This is the class that I was having trouble with passing the xyz position struct and xyz rotation struct to in it's init parameters but "fixed" by passing in 6 GLfloats instead, as shown in the above code.
When the frame has been instantiated, this frame is added to the current player's animations array (an NSMutableArray). This animations array ends up getting fairly large, as it stores all of the animation data for every joint in the model. The model I am testing with has 42 joints, each joint has 12 frames of animation, each frame has 6 GLfloats.
When the system is finished loading all of the .obj files found in the .dat file and their associated .obj and .anim files into memory, it is finished.
In the main run loop of the program, I simply loop through all of the created AEPlayers, and for each AEPLayer I loop through the player's joints array, setting the position and rotation of the NGLMesh stored at the current player's joint to whatever position / rotation is found in the player's animations array for the current joint.
I realize its a long shot to get anyone to read all of that, but I thought Id throw it out there. Also, Im aware that I have a few dead allocations in the above snippet that I need to get rid of, but they're not contributing to the problem (I added them after the problem arose to see what was happening memory wise using Instruments).
Thanks a lot for any help again,
- Adam Eisfeld
It's possible some other code is corrupting the stack, or doing something else that's undefined. If you're doing anything like that (which can be really tricky to debug), all bets are off, and simple changes might mask or unmask the real bug.
When you change between device and simulator, you're also building for ARM instead of i386 and are building optimized instead of debug. Those are all huge changes and could change the result of the undefined behavior. For example, writing one byte past the end of an array could result in no harm in a debug build, but catastrophic failure in a release build. Changing the structs to floats could change the way the parameters are passed into your method, which could either mask or unmask the bug.
You should try to isolate your problem to a small sample project. For example, can you reproduce the problem with just one class, with just that one init method?
This set of articles from the LLVM blog might be helpful if you're not already familiar with this topic:
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html
Update:
It looks like your problem may be here:
NSStringEncoding *encoding;
//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:#"dat"] usedEncoding:encoding error:&fileError];
The usedEncoding parameter of -[NSString initWithContentsOfFile:usedEncoding:error] is an out parameter, meaning that you provide storage and pass a pointer to that storage and the function will write some output to it. You've provided a pointer, but it doesn't point at anything valid, and when -[NSString initWithContentsOfFile:usedEncoding:error] writes to the usedEncoding pointer, it will overwrite something.
You should change that code to this:
NSStringEncoding encoding;
//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:#"dat"] usedEncoding:&encoding error:&fileError];
Now you've provided a pointer to a valid string encoding variable.
I have two UILabels in a XIB file, one on top of the other, the contents of which are loaded from a JSON source file on the net.
Given that I'm unsure of the number of lines the labels will take up, and hence the height, how best should I position these relative to each other.
I know in some Java GUI frameworks, one can use various container elements, and in HTML this flow of layout would be the default, but I can't find anything that would seem to do the trick.
Can I do this in Interface Builder, or does this have to be done programmatically?
Thanks for your help..
Edit:
I now have the answer, although it may not be perfect. I have two labels, titleLabel above descLabel. This is how I achieved it:
titleLabel.text = [data objectForKey:#"title"];
descLabel.text = [data objectForKey:#"description"];
CGSize s;
s.width = descLabel.frame.size.width;
s.height = 10000;
titleLabel.frame = CGRectMake(titleLabel.frame.origin.x,
titleLabel.frame.origin.y,
titleLabel.frame.size.width,
[[data objectForKey:#"title"] sizeWithFont:titleLabel.font constrainedToSize: s lineBreakMode:titleLabel.lineBreakMode].height
);
descLabel.frame = CGRectMake(descLabel.frame.origin.x,
titleLabel.frame.origin.y + [[data objectForKey:#"title"] sizeWithFont:titleLabel.font constrainedToSize: s lineBreakMode:titleLabel.lineBreakMode].height + 10,
descLabel.frame.size.width,
[[data objectForKey:#"description"] sizeWithFont:descLabel.font constrainedToSize: s lineBreakMode:descLabel.lineBreakMode].height
);
weffew
You’ll have to do this programatically. The NSString method -sizeWithFont:constrainedToSize:lineBreakMode: (which is actually in a UIKit category) will be of particular use for this.