so I'm creating a contact list app and I'm storing everything into an NSMutableArray. I've managed to get add function working perfectly. But I'm struggling with the Previous/Next functions.
I can get it to go back one object in the array when pressing previous, but i Can't get it to go back any further? here's my code: I have two extra classes, PhoneBookEntry which is a subclass of the Person class. These classes contain three strings, Firstname,lastname and studentID
- (IBAction)addPerson:(id)sender {
PhonebookEntry *person = [[PhonebookEntry alloc] init];
NSLog(#"%#",self.firstName.text);
self.currentIndex++;
person.firstName = self.firstName.text;
person.lastName = self.lastName.text;
person.phoneNumber = self.phoneNumber.text;
[self.entries addObject:person];
NSLog(#"%#", self.entries);
[self arrayLength ];
I've created a property to hold the current index.
#property (nonatomic, assign) NSInteger currentIndex;
I've created a method which then -1 from the currentINdex;
self.currentIndex = [self.entries count] - 1;
heres my button previous.
- (IBAction)btnPrevious:(id)sender {
//[self prevObject];
int length;
length = [self.entries count];
if (length > 0) {
NSLog(#"%#", self.entries[self.currentIndex--]);
// NSLog(#"object %#", [self.entries objectAtIndex:index]);
}
else {
NSLog (#"No contacts have been entered. No");
}
I can get it to go back once, but when I press it a second time Iget the following error.
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
Theres definitely data in there as I get it to print whats in the array, it also prints when I press previous also. happens no matter how many times I press the add button.
thanks
Better:
Previous Index:
// in order to go back you need to make sure that you're not already
// on index 0 and that you've items to go back to
if([self.entries count] > 1 && self.currentIndex > 0) {
self.currentIndex--;
}
Next Index:
// in order to go forward you need to make sure that you're not already
// on the last element
if(self.currentIndex < ([self.entries count] - 1)) {
self.currentIndex++;
}
Some further explanation on the root cause of your problem:
The main problem is, that you use currentIndex--. This will use the old currentIndex (which is 2) before decreasing the value. If you want to use the decreased value in this statement, you have to apply the decreasing before, i.e. by writing --currentIndex instead.
--expression = pre-decrement operator
expression-- = post-decrement operator
The pre-decrement operator does decrease the value and return the decreased value while the post-decrement operator will decrease the value by 1 and return the initial value.
i think you are starting from end of list
self.currentIndex = [self.entries count] - 1;
if current index is # 1 and you have 2 items in array adding an other will give you error
try this
NSLog(#"%#", self.entries[self.currentIndex--]);
For Previous
- (IBAction)btnPrevious:(id)sender
{
if(self.currentIndex <= 0)
{
self.currentIndex = [self.entries count] - 1;
NSLog(#"%#", self.entries[self.currentIndex])
}
else
{
self.currentIndex --;
NSLog(#"%#", self.entries[self.currentIndex])
}
}
For Next
- (IBAction)btnNext:(id)sender
{
if(self.currentIndex >= [self.entries count] - 1)
{
self.currentIndex = 0;
NSLog(#"%#", self.entries[self.currentIndex])
}
else
{
self.currentIndex ++;
NSLog(#"%#", self.entries[self.currentIndex])
}
}
Hope this will working for you.
Related
I just do the normal operation: Request the data from the server and refresh the tableview by reloadSections, but I do get a lot of crash logs. If I call reloadData, everything is OK. Even though I known reloadData works well, it seems that reloadSections works more efficiently.
Does anybody have any ideas?
Get data from the server by persistent connection :
[_pomelo onRoute:#"onTlive" withCallback:^(NSDictionary *data) {
#strongify(self);
if (self.liveTableView) {
[self.liveTableView configData:data refresh:self.playerView.isScreenPortrait];
}
}];
Then config the data and refresh the tableview:
- (void)configData:(NSDictionary *)data refresh:(BOOL)refresh {
if (!data || data.allKeys.count == 0) {
return;
}
NSArray *liveArray = [data valueForKeyPath:#"body.data"];
[liveArray enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSArray *liveDetailInfo = [obj componentsSeparatedByString:#"|"];
if (liveDetailInfo && liveDetailInfo.count > 0) {
NSArray *tliveInfo = [liveDetailInfo.lastObject componentsSeparatedByString:#"^"];
SPBasketballLive *liveObject = [SPBasketballLive new];
liveObject.type = tliveInfo[0];
liveObject.time = tliveInfo[1];
liveObject.team = tliveInfo[2];
liveObject.playerId = tliveInfo[3];
liveObject.score = tliveInfo[4];
liveObject.text = tliveInfo[5];
liveObject.quarter = ((NSString *)liveDetailInfo.firstObject).integerValue;
if (self.curMatch.liveInfo.count == liveObject.quarter) {
NSMutableArray *quarterArray = self.curMatch.liveInfo.firstObject;
[quarterArray insertObject:liveObject atIndex:0];
} else {
NSMutableArray *quarterArray = [NSMutableArray new];
[quarterArray addObject:liveObject];
[self.curMatch.liveInfo insertObject:quarterArray atIndex:0];
}
}
}];
if (refresh) {
if (_curMatch.liveInfo.count > 0) {
[self.myTableView reloadSections:[NSIndexSet indexSetWithIndex:4] withRowAnimation:UITableViewRowAnimationNone];
}
}
}
Here is the crash log:
invalid update: invalid number of rows in section 4. The number of rows contained in an existing section after the update (98) must be equal to the number of rows contained in that section before the update (96), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)
Your app crashed because reloadSection method won't update section and row counters so it causes crashes in cases when you changed section or row counter and your UITableView have different counters.
And when you use reloadData it first call numberOfSectionsInTableView and then numberOfRowsInSection is called so your UITableView has correct counters for sections and rows.
This question already has answers here:
Removing object from NSMutableArray
(5 answers)
Closed 7 years ago.
My program has a NSMutable Array named as "matchedCards", and I have added few object in it of type Card, now I need to remove the objects from the array, and I use the following code for it:
for (Card * removeCards in matchedCards)
{
[self.matchedCards removeObject:removeCards];
}
The first card-object gets removed , and after that the program gets crashed , Can anyone explain the reason behind it, if it successfully removes the first object, why it starts throwing error from 2nd object onwards
You can't remove elements from an array while fast-enumerating it.
If you simply want to remove all objects do
[self.matchedCards removeAllObjects];
If you want to remove only certain elements however, remember their indices in an IndexSet and remove those
NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet new];
for (NSUInteger index = 0; index < [self.matchedCards count]; ++index) {
if (whatever) {
[indexesToRemove addObject:index];
}
}
[self.matchedCards removeObjectsAtIndexes:indexesToRemove];
You can't remove an object from an array while iterating on it. Do this instead:
for (Card * removeCards in [matchedCards copy])
{
[self.matchedCards removeObject:removeCards];
}
Read the crash log. It will say something along the lines of...
"Collection was stated while being enumerated"
Or something like that.
You can't mutate an array while iterating over it using a for:in loop.
You can do this though...
[matchedCards enumerateObjectsUsingBlock:^(Card *removedCards, NSInteger idx, BOOL *stop) {
[self.matchedCards removeObject:card];
}];
Also, with your current code you are actually removing all of the objects from the matchedCards array. It will result in an empty array. Are you sure that's what you want?
The reason is because you are removing the current object and ruin the for-statement
Here's a solution:
for (int i = 0; i < self.matchedCards.count; i++)
{
if ([self.matchedCards[i] isKindOfClass:[YourClass class]])
{
[self.matchedCards removeObject:self.matchedCards[i]];
i--; // invalidate the removed index
}
}
NSLog(#"%#", self.matchedCards);
Take note that i-- is important, else you will not get through to the last element of the array..
Hope this helps you.. Cheers..
// if remove all objects
[matchedCards removeAllObjects];
// if you want to remove using index
for (int i =[matchedCards count]-1; i>=0; i++) {
if (condition) {
[matchedCards removeObjectAtIndex:i];
}
}
so I'm creating a contact list app and I'm storing everything into an NSMutableArray. I've managed to get add function working perfectly. But I'm struggling with the Previous/Next functions.
I can get it to go back one object in the array when pressing previous, but i Can't get it to go back any further? here's my code: I have two extra classes, PhoneBookEntry which is a subclass of the Person class. These classes contain three strings, Firstname,lastname and studentID
- (IBAction)addPerson:(id)sender {
PhonebookEntry *person = [[PhonebookEntry alloc] init];
NSLog(#"%#",self.firstName.text);
person.firstName = self.firstName.text;
person.lastName = self.lastName.text;
person.phoneNumber = self.phoneNumber.text;
[self.entries addObject:person];
Here's my Previous button:
int length;
length = [self.entries count];
if (length > 0) {
int index;
index = [self.entries count] - 1;
NSLog (#"%d the index is", index);
NSLog(#"object %#", [self.entries objectAtIndex:index]);
} else {
NSLog (#"No contacts have been entered. No");
}
//NSLog(#"%d is the length - 1 hopefully", length);
//NSLog (#"index at %d is ", length);
I've tried removing the - 1 here:
index = [self.entries count] - 1;
and changing then on the next line putting index--; but nothing seems to work. it just goes back once.
I understand that length is getting the amount of objects in the index, and then -1 but shouldnt i-- at the end of the count / - 1 keep removing it everytime its pressed??
Any ideas? Cheers
You're going to keep hitting the same index with that piece of code - you need to store the state somewhere. Who is going to hold onto what the current index is? With a good designed system the model should really take care of this.
You are best off storing the current index into an ivar and updating that every time the button is pressed.
#interface OKAClass ()
#property (nonatomic, assign) NSInteger currentIndex;
#end
//... initialise the property with a default value
self.currentIndex = [self.entries count] - 1;
//... when the button is pressed decrement the index (you might want some min / max validation or use modulus to loop over and start from the top again)
NSLog(#"%#", self.entries[self.currentIndex--]);
I've probably been starting at this too long and simply can't see the logic problem. I'm changing background images on a pan gesture. Swiping left/right cycles thru an array of image names for the background and loops.
Swiping Right (increasing) works fine, it loops back to the start of my array.
Swiping Left (decreasing stops at the first object in my array (objectIndex: 0).
NSLog(#"_imageBackgroundIndex Before:%d",_imageBackgroundIndex);
if ([_panDirection isEqual:#"Right"])
{
_imageBackgroundIndex = _imageBackgroundIndex + 1;
}
if ([_panDirection isEqual:#"Left"])
{
_imageBackgroundIndex = _imageBackgroundIndex - 1;
}
NSLog(#"_imageBackgroundIndex After:%d",_imageBackgroundIndex);
if (_imageBackgroundIndex > ([_backgroundImages count] - 1))
{
_imageBackgroundIndex = 0;
}
if (_imageBackgroundIndex < 0)
{
_imageBackgroundIndex = ([_backgroundImages count] - 1);
}
[[self childNodeWithName:kBackgroundName] runAction:
[SKAction setTexture:
[SKTexture textureWithImageNamed:
[_backgroundImages objectAtIndex:_imageBackgroundIndex]]]];
Anyone see the issue?
Code looks okay (though there are some style improvements to consider once you get it working). I'd wager that you have _imageBackgroundIndex declared as an unsigned int or NSUInteger. When you think it drops below zero, it's really taking on a huge unsigned value.
This code will work (and has better style) even if the counter is declared unsigned...
- (void)incrementIndex {
BOOL increment = self.imageBackgroundIndex < self.backgroundImages.count-1;
self.imageBackgroundIndex = (increment)? self.imageBackgroundIndex+1 : 0;
}
- (void)decrementIndex {
BOOL decrement = self.imageBackgroundIndex > 0;
self.imageBackgroundIndex = (decrement)? self.imageBackgroundIndex-1 : self.backgroundImages.count-1;
}
Then your pan method becomes simpler:
// not sure how panDirection is initialized, is it really a string #"Left" or #"Right"?
// if so, use isEqualToString to compare strings
if ([_panDirection isEqualToString:#"Right"]) [self incrementIndex];
else if ([_panDirection isEqualToString:#"Left"]) [self decrementIndex];
[[self childNodeWithName:kBackgroundName] runAction:// ... etc
I have a NSMutableArray of N Integer elements (N>4), I want to get 3 different random elements from this array. I do not really need a perfectly-uniform distribution, just 3 different random elements should be OK. Do you have any suggestion? Thanks
Make NSIndexSet, and keep adding
int value = arc4random() % array.count;
items to it until its size gets to 3. The you know that you have your three indexes.
NSMutableIndexSet *picks = [NSMutableIndexSet indexSet];
do {
[picks addIndex:arc4random() % array.count];
} while (picks.count != 3);
[picks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"Element at index %ud: %#", idx, [array elementAt:idx]);
}];
for (int i = 1; i <= 3; i++) {
int index = (int)(arc4random() % [array count]);
id object = [array objectAtIndex:index];
[array removeObjectAtIndex:index];
}
arc4random() returns a random number in the range [0,2^32-1). The remainder when you take the modulus with the size of the array gets you a value between [0,arrayCountLessOne].
If you don't want to change your original data array, you can just make a copy of the array.
If you want to do this more than once from various places in your code then consider doing this: The "Objective C way" is to create a category on NSMutableArray which adds a method randomObjects. The method itself should generate three random integers from 0 to the length of the array -1 (N-1), then return a set of objects from the array at those indices, as per the other answers here (dasblinkenlight's in particular.)
First, create the category. Create a new header file NSMutableArray+RandomObject.h, containing:
#interface NSMutableArray (RandomObjects)
- (NSSet *) randomObjects;
#end
The RandomElement in parentheses is the name of your category. Any class you write that includes this new header file will give all your NSMutableArray instances the randomElement method.
Then the implementation, in NSMutableArray+RandomObjects.m:
#implementation NSMutableArray (RandomObjects)
- (NSSet *) randomObjects {
// Use the code from #dasblinkenlight's answer here, adding the following line:
return picks;
}
#end
And that's basically it. You've effectively added that capability to NSMutableArray.
Nice answer from dasblinkenlight!
In Swift 4:
let indexes = NSMutableIndexSet()
if array.count > setSize {
repeat {
indexes.add(Int(arc4random_uniform(UInt32(array.count))))
} while (indexes.count < setSize)
}