I have a method which can only work with an array of maximum 100 objets.
However I have an array with more than 100 objects, how could I call this method multiple times so it will work for arrays which have more than 100 objets?
Let's say I have an array of 264 objects.
I could call the method 3 times like so:
[self doSomething:[arrayWithMoreThan100Objects subarrayWithRange:NSMakeRange(0, 100)];
[self doSomething:[arrayWithMoreThan100Objects subarrayWithRange:NSMakeRange(100, 100)];
[self doSomething:[arrayWithMoreThan100Objects subarrayWithRange:NSMakeRange(200, 64)];
This works fine, however I don't always know the size of the array the method needs to process so calling this manually wont work.
How could I get around this?
Thanks!
Use a loop:
NSInteger increment = 100;
for (NSInteger start = 0; start < arrayWithMoreThan100Objects.count; start += increment) {
NSInteger length = MIN(increment, arrayWithMoreThan100Objects.count - start);
NSRange range = NSMakeRange(start, length);
[self doSomething:[arrayWithMoreThan100Objects subarrayWithRange:range];
}
The value of length is set to either 100 or a smaller value if there are less than 100 objects left in the array.
Related
My code contains four NSArrays, each of which contains two objects which represent XY-Coordinate at any point on the screen. Two or more arrays may contain same set of coordinates. I need to find the coordinate which has highest number of repetition among these four arrays. Can the isEqualTo: method help in this case?
One approach would be to maintain an NSDictionary, use the coordinates in your arrays as keys and then maintain a counter for each coordinate (i.e. key) that you increment whenever you see the same coordinate.
This could look somewhat like this:
NSMutableDictionary *coordinateCount = [[NSMutableDictionary alloc] init];
for (int i = 0; i < coordinates.length; i++) { // do this loop for each of your 4 arrays
Coordinate *c = coordinates[i];
if ([coordinateCount containsKey:c]) {
NSInteger count = [coordinateCount[c] integerValue];
count++;
coordinateCount[c] = #(count);
}
else {
coordinateCount[c] = #(1);
}
}
// now you can retrieve the max count value from all collected values
Note that this code is not tested and has to be adjusted depending on your types and variable names.
I have an array called '_orderOfCardPlacement' which gets objects put in it after every turn of my game. These objects are UIButtons which have a number of details such as setImage, setBounds, setCenter and most importantly to this question setTag. There are two possible outcomes for setTag either '1' or '2', They are initially set with one or the other and this can be changed via another method which is called every turn. What i need to do is look through this array and see how many times '1' has come up and how many times '2' has come up and then compare that result. So if '1' comes up nine times and '2' comes up 7 times then '1' wins in this case. So when i NSLog my array (say after one turn) i get this:
"<OBShapedButton: 0x7ff6f968e6a0; baseClass = UIButton; frame = (103 387.5; 150 135); opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; tag = 1; layer = <CALayer: 0x7ff6f968e8a0>>"
and subsequent turns will add more buttons to the array. But I need to know once this array is full and the game is complete how to access the 'tag = ' part of the objects to compare. Thanks for any advice, ill add more code if necessary!
Here's the solution in full, if there's a more elegant way to do what i'm doing here, definitely interested to know!
//set the amount of cards each player has
_howManyCardsForComputer = 0;
_howManyCardsForPlayer = 0;
for (int i = 0; i < _orderOfCardPlacement.count; i++) {
UIButton *auxButton = (UIButton *) [_orderOfCardPlacement objectAtIndex:i];
NSInteger playerScore = auxButton.tag;
NSMutableArray *totalScoreArray = [[NSMutableArray alloc] init];
[totalScoreArray addObject:[NSNumber numberWithInteger:playerScore]];
for (int j = 0; j < totalScoreArray.count; j++) {
if ([[totalScoreArray objectAtIndex:j] isEqualToNumber:[NSNumber numberWithInteger:1]]) {
_howManyCardsForPlayer++;
}
if ([[totalScoreArray objectAtIndex:j] isEqualToNumber:[NSNumber numberWithInteger:2]]) {
_howManyCardsForComputer++;
}
}
}
_playerScore.text = [NSString stringWithFormat:#"Players cards %i", _howManyCardsForPlayer];
_playerScore.hidden = NO;
_computerScore.text = [NSString stringWithFormat:#"Computers cards %i", _howManyCardsForComputer];
_computerScore.hidden = NO;
I am not sure if I understood clearly what you want to achieve. If your buttons are stored in a NSMutableArray, you can check their tags by geting the i-th element (inside a for-loop) and apply a casting.
NSMutableArray *arrayWithButtons = [[NSMutableArray alloc] init];
//... Fill the array
//Check tags
for(int i=0; i<numButtons; i++){
UIButton *auxButton = (UIButton*) [arrayWithButton objectAtIndex:i];
int auxTag = auxButton.tag;
//... perform the proper operations
}
I hope I understand it well, and this can help you.
I want to be able to update / reload a ScatterPlot on iOS Devices during runtime. To be specific, I record Audio input, do some funny stuff and put the result as a simple NSNumber value into an array. Whenever that happens, reloadData is called on the Plot I want to update but sadly, exactely nothing happens. Basically, I do the same as described in the answer here: real time plotting on iPhone using core plot? . It just doesn't work, so I assume I made some stupid mistake.
This the relevant method:
-(NSNumber *) numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index {
double val = (index/5.0) -10;
if (fieldEnum == CPTScatterPlotFieldX) {
return [NSNumber numberWithDouble:val];
} else {
if ([plot.identifier isEqual: #"X Squared Plot"]) {
return [NSNumber numberWithDouble:10-(0.25*val*val)];
} else if( [plot.identifier isEqual:#"LivePlot"]) {
if (!_isMeasuring) {
return [NSNumber numberWithFloat:0.0f];
} else {
printf("%f", [[_plotIndizesAndVolume objectAtIndex:index] floatValue]);
return [NSNumber numberWithFloat:[[_plotIndizesAndVolume objectAtIndex:index] floatValue]];
}
} else {
return 0;
}
}
}
The X Squared Plot is irrelevant to my needs for now, it's just a reference. The LivePlot is the one I need and want to update. The _plotIndizesAndVolume is the NSMutableArray I store my values in. It has 500 Elements, all of which are initialized with [NSNumber numberWithFloat:0.0f] at the beginning of my execution. The Plot has 500 indizes as well, therefor we don't get out of bounds or whatever.
The code executes fine, the method is being called, the printf statement works and correctly displays the floats, the Plot just doesn't update.
I also tried
if (index < [_plotIndizesAndVolume count]) {
return [NSNumber numberWithFloat:[_plotIndizesAndVolume objectAtIndex:index]];
} else {
return [NSNumber numberWithFloat:0.0f];
}
without having the 500 0.0fs in the array, so that I just access values differently from 0.0f. This of course is better, it just doesn't work either.
Any ideas?
Update 1:
If I fill the _plotIndizesAndVolume with just random numbers at the beginning of my application, the plot perfectly follows these numbers.
If I printf in the method I can perfectly read the updated values once reload has been called on the plot.
However, these updated values aren't shown in the plot. The plot remains the way it is after calling -reloadData on it, no matter which values changed in the _plotIndizesAndVolume-Array.
So, from what I can see: I can access the array properly, I can update and read the new values properly, the plot still stays the same and doesn't change. I'm confused. Probably I still made some random stupid mistake, I just don't see it.
Update2:
Well, it seems as if my problem isn't the reloadData itself, but from where I call it. Some more context: I'm using novocaine to measure decibel via the device microphone. This works fine. I then want to visualize part of the measured dB in a graph through corePlot (which I described above). I got a method called startMeasuring which I call somewhen and then - obviously - start measuring decibel. Whenever that happens I put the measured value int _plotIndicesAndVolume and call reloadData on the plot.
The method looks like this (and is largely copied directly from the novocain examples):
-(void) startMeasuring {
if (_isMeasuring) {
return;
}
__weak ViewController * wself = self;
self.ringBuffer = new RingBuffer(32768, 2);
self.audioManager = [Novocaine audioManager];
// MEASURE SOME DECIBELS!
// ==================================================
__block float dbVal = 0.0;
__block int count = 0;
__block int limiter = 0;
[self.audioManager setInputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels) {
vDSP_vsq(data, 1, data, 1, numFrames*numChannels);
float meanVal = 0.0;
vDSP_meanv(data, 1, &meanVal, numFrames*numChannels);
float one = 1.0;
vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);
dbVal = dbVal + 0.2*(meanVal - dbVal);
float reducedVal = (dbVal + 66.0f)/3;
if (!wself.measuringPaused && limiter > 10) {
[wself.plotIndizesAndVolume replaceObjectAtIndex:count withObject:[NSNumber numberWithFloat:reducedVal]];
[[wself.graph plotWithIdentifier:#"LivePlot"] reloadData];
limiter = -1;
++count;
}
++limiter;
}];
[self.audioManager play];
_isMeasuring = true;
}
I call reloadData from within the measuring block. I don't know exactely much about using blocks, I just assumed I could do that. If I, however, try to change the data in the array and afterwards reload the plot manually from somewhere else, it works and the plot updates accordingly. Is the problem related to my call of the method from within the block?
Even then I'm still unsure why it doesn't work, as the printf statement works perfectly - so I'm under the assumption that the updateData method is correctly invoked and the values are correctly updated.
Update 3:
If I call reloadData from somewhere else during the measurement, the graph updates just fine and according to whatever is in _plotIndizesAndVolume. It simply doesn't update if I call it from inside the block (even if I call another method from within the block which in turn calles reloadData). However, I need the live update. I assume I could reload the plot once every x miliseconds as well (going to try that next), I'm still curious as to why it doesn't work in the first place.
Update 4: As assumed in Update 3, if I call reloadData from somewhere else through a NSTimer, it does exactely what I want it to do. Why can't I call it from within the block then?
Call -reloadData (and any other Core Plot methods) from the main thread.
Probably you need to update your plot space x-range/y-range in your timer
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)self.graph.defaultPlotSpace;
[plotSpace scaleToFitPlots:[self.graph allPlots]];
CPTPlot *thePlot = [self.graph plotWithIdentifier:kIdentifier];
[thePlot reloadData];
Even though I’m writing in Objective C, most of my code is still written in a procedural style. However, now I want to do something where that approach will not work. So I need some advice on how to deal with an indeterminate number of objects on the screen at the same time. I’m sure that this problem has been solved, I just haven’t been able to find out how.
I have a bunch of games where I put two or four pictures on the screen and then the user interacts with the picture. When they are done with a page they swipe to the next one and I use a transition to slide the pictures off the screen. I can control the movement of the pictures because when they were created I name them self.picture_1 and self.picture_2. The movement method knows about them even though that method didn’t create them.
Now suppose I want to have an indeterminate number of pictures on the screen. I can’t call them self.picture_1. through self.picture_n because ObjectiveC won’t let you dynamically create variable names. But I still need to move them in a method where they weren’t created.
I can make it work with two techniques, neither of which seem ideal. First, I look at all the objects on the screen and then do something with the ones that I want to target. Note: pictures are in buttons.
for ( id subview in self.parentView.subviews ) {
if ( [subview isKindOfClass:[UIButton class]] ) {
UIButton *pictureButton = subview;
for (NSUInteger i=0; i<self.totalItems; i++) {
NSUInteger row = (i % 2) + 1;
NSUInteger column = (i/2) + 1;
NSString *pictureTitle = [NSString stringWithFormat:#"pictureR%iC%i", row, column];
if ( [pictureButton.titleLabel.text isEqualToString:pictureTitle] ) [pictureButton removeFromSuperview];
}
}
}
This works for removing them from the view, but gets cumbersome when I try to make the pictures slide off the screen.
The second way is to make an array that holds the picture objects when they are created. I’ve been playing with something like this.
self.gridImages = [NSMutableArray arrayWithCapacity:4];
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Word *word = [wordListArray objectAtIndex:i];
self.gridImages[i] = word.image;
}
And then to do things with the pictures I loop through the array.
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Picture *picture = self.gridImages[i];
// do something with picture
}
Neither of these methods seems ‘right’ so I’m wondering if there is a preferred method for manipulating an indeterminate number of objects on the screen?
#Hot Licks, that works, so I put you answer into an answer.
I have it working for checkBoxes. They are created by the main view controller and it passes in a tag. I'm using tags starting at 1000 for checkboxes, 2000 for pictures, etc. Just before I put the checkBox on the screen, I assign it the tag.
[self.checkBox setTag:checkBoxTag];
You could also use: self.checkbox.tag = checkBoxTag;
When it is time to remove the checkBoxes, I loop through all of the tags starting at 1000 up to the total number of items on the screen. I have warnings turned up to 11 so I need to cast the counter to an NSInteger.
- (void)removeCheckboxes {
for (NSUInteger i = 0; i < self.totalItems; i++) {
NSInteger tagNumber = 1000 + (NSInteger)i;
[ [self.parentView viewWithTag:tagNumber] removeFromSuperview];
}
}
I have 10 UIImageViews on the screen. They are all in an array called posArray. I also have another UIImageView that is dragged by user and can hit those other 10. What is the easies way of displaying a simple NSlog message if the one object hits any of the other 10?
Right now i'm using touchesBegin, touchesMoved to move my one object and this array below to test if the one objects hits any of the other ten.
I'm just thinking that there is an easier, less memory spending way, way of doing this for some reason.
for (int i = 0; i < [posArray count]; i++) {
UIImageView *tempPos;
tempPos = [posArray objectAtIndex:i];
if (CGRectIntersectsRect(red1.frame, tempPos.frame)) {
red1.center = CGPointMake(tempPos.center.x, tempPos.center.y);
NSLog(#"position piece touched");
}
}
You can also use fast enumeration to get some more speed. Also you can add a break statement after you found one match (if you just need one match):
for (UIImageView * tempPos in posArray){
if (CGRectIntersectsRect(red1.frame, tempPos.frame)) {
red1.center = CGPointMake(tempPos.center.x, tempPos.center.y);
NSLog(#"position piece touched");
break;
}
}