I have created a array in my controller. Based on that JSON array my cards[tinderswipecards/cwRichard] are created . Now I want if I remove a card I want to get the data of card Shown to display somewhere in my viewController something like this
My cards coming from DraggableBackgroundView Class and i m implementing this in my own viewController
_peopleNearBypeopleList = [NSMutableArray new];
[dataArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
PeopleNearByIndividualModel * model = [PeopleNearByIndividualModel new];
NSDictionary *dicData = obj;
model.senderId = [[dicData objectForKey:USER_ID] integerValue];
model.displayName = [[dicData objectForKey:DISPLAY_NAME] uppercaseString];
model.userImage = [dicData objectForKey:#"image"];
[self.peopleNearBypeopleList addObject:model];
NSLog(#"%#",_peopleNearBypeopleList);
if (idx == [dataArray count] - 1) {
[self createInvitationViews];
}
and the strings to be created like
int i;
for (i = 0; i < [_peopleNearBypeopleList count]; i++) {
PeopleNearByIndividualModel * model = [_peopleNearBypeopleList objectAtIndex:i];
NSString *nameString = #"ARE YOU SURE YOU WANT TO INVITE ";
nameString = [nameString stringByAppendingString:model.displayName];
NSLog(#"%#", nameString);
_textView.textColor = [UIColor whiteColor];
_textView.font = [UIFont fontWithName:#"Roboto-Regular" size:17.0f];
_textView.text = nameString;
}
again and it is calling the first Object.how can i acheive this
this is my draggableViewBackground Class
//%%% loads all the cards and puts the first x in the "loaded cards" array
-(void)loadCards:(NSArray*)array
{
exampleCardLabels = array;
if([exampleCardLabels count] > 0) {
NSInteger numLoadedCardsCap =(([exampleCardLabels count] > MAX_BUFFER_SIZE)?MAX_BUFFER_SIZE:[exampleCardLabels count]);
//%%% if the buffer size is greater than the data size, there will be an array error, so this makes sure that doesn't happen
//%%% loops through the exampleCardsLabels array to create a card for each label. This should be customized by removing "exampleCardLabels" with your own array of data
for (int i = 0; i<[exampleCardLabels count]; i++) {
MG_DraggableVIew* newCard = [self createDraggableViewWithDataAtIndex:i];
[allCards addObject:newCard];
if (i<numLoadedCardsCap) {
//%%% adds a small number of cards to be loaded
[loadedCards addObject:newCard];
}
}
//%%% displays the small number of loaded cards dictated by MAX_BUFFER_SIZE so that not all the cards
// are showing at once and clogging a ton of data
for (int i = 0; i<[loadedCards count]; i++) {
if (i>0) {
[self insertSubview:[loadedCards objectAtIndex:i] belowSubview: [loadedCards objectAtIndex:i-1]];
} else {
[self addSubview:[loadedCards objectAtIndex:i]];
}
cardsLoadedIndex++; //%%% we loaded a card into loaded cards, so we have to increment
}
}
// }
}
#warning include own action here!
//%%% action called when the card goes to the left.
// This should be customized with your own action
-(void)cardSwipedLeft:(UIView *)card;
{
//do whatever you want with the card that was swiped
// DraggableView *c = (DraggableView *)card;
[loadedCards removeObjectAtIndex:0]; //%%% card was swiped, so it's no longer a "loaded card"
if (cardsLoadedIndex < [allCards count]) { //%%% if we haven't reached the end of all cards, put another into the loaded cards
[loadedCards addObject:[allCards objectAtIndex:cardsLoadedIndex]];
cardsLoadedIndex++;//%%% loaded a card, so have to increment count
[self insertSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-1)] belowSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-2)]];
}
}
#warning include own action here!
//%%% action called when the card goes to the right.
// This should be customized with your own action
-(void)cardSwipedRight:(UIView *)card
{
//do whatever you want with the card that was swiped
// DraggableView *c = (DraggableView *)card;
[loadedCards removeObjectAtIndex:0]; //%%% card was swiped, so it's no longer a "loaded card"
if (cardsLoadedIndex < [allCards count]) { //%%% if we haven't reached the end of all cards, put another into the loaded cards
[loadedCards addObject:[allCards objectAtIndex:cardsLoadedIndex]];
cardsLoadedIndex++;//%%% loaded a card, so have to increment count
[self insertSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-1)] belowSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-2)]];
}
}
//%%% when you hit the right button, this is called and substitutes the swipe
-(void)swipeRight
{
MG_DraggableVIew *dragView = [loadedCards firstObject];
dragView.overlayView.mode = GGOverlayViewModeRight;
[UIView animateWithDuration:0.2 animations:^{
dragView.overlayView.alpha = 1;
}];
[dragView rightClickAction];
}
//%%% when you hit the left button, this is called and substitutes the swipe
-(void)swipeLeft
{
MG_DraggableVIew *dragView = [loadedCards firstObject];
dragView.overlayView.mode = GGOverlayViewModeLeft;
[UIView animateWithDuration:0.2 animations:^{
dragView.overlayView.alpha = 1;
}];
[dragView leftClickAction];
}
you can post a NSNotification inside -(void)cardSwipedLeft:(UIView *)card & -(void)cardSwipedRight:(UIView *)card method in DraggableViewBackground.
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:index] forKey:#"someKey"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:self userInfo:userInfo];
Here index is your card index. To set the index, you can declare a int property named index. Set initial value value of index like index=0; in initwithframe method.
then, increase the value of index when you swipe a card.
set index++; inside -(void)cardSwipedLeft:(UIView *)card & -(void)cardSwipedRight:(UIView *)card method in DraggableViewBackground.
Listen for this notification in your view controller. To achieve that add in the view controller's method in viewDidLoad by this line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
implement this selector method in your viewcontroller-
- (void) receiveTestNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"TestNotification"]){
NSLog (#"Successfully received the test notification!");
NSDictionary *userInfo = notification.userInfo;
int myObject = [[userInfo objectForKey:#"someKey"]integerValue];
NSLog(#"index: %d", myObject);
}
}
on receiveTestNotification method you get card index. then you can get your data from your array by index.
In my opinion, it will be better for you if you maintain a dictionary for cards and their data.
You can create a dictionary with keys as card ids/card numbers and values as their data (description.)
So whenever you are removing card, simply fetch its data from dictionary.
Lets say cardsDictionary is your dictionary like this :
cardsDictionary = #{#"card1_id":#"card1_data"......#"cardn_id":#"cardn_data"};
Then on removing a card you can do like this:
card_data = cardsDictionary[#"card_id"];
Related
I made a bug when I implement the search function. I opened an asynchronous thread. But when deleting a character (a digit of a phone number), the app would crash.
Error:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x117d7320> was mutated while being enumerated.'
Code:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
//
self.isSearch = YES;
//remove the last search all the contacts
[self.resultArr removeAllObjects];
//
[self.rcs_SearchTableView reloadData];
//
dispatch_queue_t uploadQueue = dispatch_get_global_queue(0, 0);
dispatch_queue_t getMainQueue = dispatch_get_main_queue();
dispatch_async(uploadQueue, ^{
NSMutableArray *phoneArr = (NSMutableArray *)[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText];
//
//NSLog(#"清空上次搜索的数据:%#", self.resultArr);
//NSLog(#"输入的关键字是---%#---%lu",searchText,(unsigned long)searchText.length);
if (0 == searchText.length || [searchText isEqualToString:#" "]) {
self.isSearch = NO;
//[self.rcs_SearchTableView reloadData];
[self.resultArr removeAllObjects];
}
//[self.rcs_SearchTableView reloadData];
if (0 != phoneArr.count) {
//
for (NSUInteger i = 0; i < phoneArr.count; i ++) {
RCSPhoneModel *flagPhoneModel = phoneArr[i];
for (NSUInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *flagModel = self.rcsRecentSearchDataSource[i];
if ([flagPhoneModel.serverId isEqualToString:flagModel.serverId] || [flagPhoneModel.phone isEqualToString:flagModel.name]) {
//the same contact has multiple Numbers To prevent repeated add the same contacts
if (![self.resultArr containsObject:flagModel]) {
[self.resultArr addObject:flagModel];
continue;
}
}
}
}
}else{
//search contacts by name
for (NSInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *model = self.rcsRecentSearchDataSource[i];
NSString *nameStr = model.name;
if (nameStr.length >= searchText.length) {
//search all the name
if ([nameStr containsString:searchText]) {
[self.resultArr addObject:model];
}
}
}
}
//
if (self.resultArr.count > 0) {
self.isSearch = YES;
//[self.rcs_SearchTableView reloadData];
}
//The phone contacts or local contact synchronized to the server
dispatch_async(getMainQueue, ^{
[self.rcs_SearchTableView reloadData];
});
});
}
A for loop should not enumerate anything that could change on any other thread or that could change within that loop. You should only enumerate an object that you are certain is not going to change while being enumerated (either in another thread, or within the loop itself). One way to do this is to only use a local copy of the array to enumerate over.
I can't see where anything being enumerated in your for loops is changed within the loop, so I would guess that in some other code in some other thread, you are changing either self.rcsRecentSearchDataSource or phoneArr. This crashes the for loop that enumerates self.rcsRecentSearchDataSource or phoneArr because it is required to not change while being enumerated.
Does this really need to be run on a separate thread?
If so, use a thread-local copy of the array to enumerate over, instead of the original array. That way you can be sure that nothing else can modify it, because it does not exist in any other scope.
Eg, there are two places where you could change your code to:
NSArray *localSearchDataSource = [self.rcsRecentSearchDataSource copy];
for (NSUInteger i = 0; i < localSearchDataSource.count; i ++) {
and one place where you could change to:
NSArray *localPhoneArr = [[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText] copy];
for (NSUInteger i = 0; i < localPhoneArr .count; i ++) {
I got it answer and like unders codes:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
//
self.isSearch = YES;
//
if (0 == searchText.length || [searchText isEqualToString:#" "]) {
self.isSearch = NO;
//[self.resultArr removeAllObjects];
}
//Remove the last search all the contacts
[self.resultArr removeAllObjects];
//
[self.rcs_SearchTableView reloadData];
//
NSMutableArray *localSearchDataSource = [self.resultArr mutableCopy];
//Create an array of objects as well as the original array
//According to the input access to the phone number of the data
NSArray *localPhoneArr = [[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText] copy];
//
//[self.rcs_SearchTableView reloadData];
if (0 != localPhoneArr.count) {
//Because when data matching number takes longer, using asynchronous thread
dispatch_queue_t uploadQueue = dispatch_get_global_queue(0, 0);
dispatch_queue_t getMainQueue = dispatch_get_main_queue();
dispatch_async(uploadQueue, ^{
//
for (NSUInteger i = 0; i < localPhoneArr.count; i ++) {
RCSPhoneModel *flagPhoneModel = localPhoneArr[i];
for (NSUInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *flagModel = self.rcsRecentSearchDataSource[i];
if ([flagPhoneModel.serverId isEqualToString:flagModel.serverId] || [flagPhoneModel.phone isEqualToString:flagModel.name]) {
//The same contact has multiple Numbers To prevent repeated add the same contacts
if (![localSearchDataSource containsObject:flagModel]) {
[localSearchDataSource addObject:flagModel];
}
}
}
}
//Add the search results to the search data source
dispatch_async(getMainQueue, ^{
[self.resultArr addObjectsFromArray:localSearchDataSource];
[self.rcs_SearchTableView reloadData];
});
});
}else{
//Search contacts by name
for (NSInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *model = self.rcsRecentSearchDataSource[i];
NSString *nameStr = model.name;
if (nameStr.length >= searchText.length) {
//Search all name
if ([nameStr containsString:searchText]) {
[self.resultArr addObject:model];
}
}
}
}
//
if (self.resultArr.count > 0) {
self.isSearch = YES;
[self.rcs_SearchTableView reloadData];
}
}
I made two change that 'NSMutableArray *localSearchDataSource = [self.resultArr mutableCopy];' and 'NSArray *localPhoneArr = [[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText] copy];'. And finish it . Collection <__NSArrayM: 0x117d7320> was mutated while being enumerated.'
I have a program where a user enter strings into an NSMutableArray (myArray) via a Text Field. This array is passed into the next view controller where there is a label (myLabel) and two buttons. Printed to the label is a random string from myArray. ButtonA displays a different random string from the array when pressed and ButtonB removes the current string that is printed to the label and then displays a random string from the array to the label.
This is my current solution:
- (void)viewDidLoad {
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
-(IBAction)ButtonA:(id)sender {
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
}
-(IBAction)ButtonB:(id)sender {
NSInteger index = [myArray indexOfObject: //what goes here?];
[self.myArray removeObjectAtIndex:index];
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
}
Is there a way to get the index of the random string displayed and then remove it from the array? I want this to continue doing this until all items from the array have been removed. Thank you
The // what goes here? should simply be self.myLabel.text.
Though it might be better to add an instance variable that saves off the last random index. Then all array index references should be made with that instance variable.
You also have the same line of code to calculate a random number and set a label repeated 3 times. Create a new method for that and call that function from the three places you have it now.
There are two ways you can do this:
The first way is to store the string you got from your random method. You can declare a global variable for this in your class. And I suggest that you always place a block of similar code in another method.
NSString *generatedString;
- (NSString *)generateRandomString
{
generatedString = [self.myArray objectAtIndex:arc4random() % [myArray count]];
return generatedString;
}
Then in your implementation:
- (void)viewDidLoad
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
[self.myArray removeObject:generatedString];
self.myLabel.text = [self generateRandomString];
}
Another way is to store the index of the string generated:
NSInteger generatedStringIndex;
- (NSString *)generateRandomString
{
generatedStringIndex = arc4random() % [myArray count];
NSString generatedString = [self.myArray objectAtIndex:generatedStringIndex];
return generatedString;
}
Then in your implementation:
- (void)viewDidLoad
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
[self.myArray removeObject:generatedStringIndex];
self.myLabel.text = [self generateRandomString];
}
So, I have a UIPicker view which gets populated from a NSMutableArray as long as the input is not "NULL".
So my picker shows all the values except NULL.
Now, I have a UITextField box and a button. So whatever I type in the text field, and I click the button, if it matches to anything which was there in the NSMutableArray ( which was used to populate UIPickerView ), it sets it to NULL and refreshes the UIPicker so that it doesn't get displayed anymore.
For some reason, I'm able to set the value to NULL(checked using NSLog), but the picker never gets updates, and neither does the NSMutable Array.
-(void) loadthepicker
{
NSMutableArray *getarray = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"FilerNamesArray"]];
pickerLoaderArray=[[NSMutableArray alloc] init];
for (int j=0; j<20; j++) {
if ([[getarray objectAtIndex:j] isEqualToString:#"NULL"])
{
// do nothing..don't load
}
else // add that filter to pickerLoaderArray
{
[pickerLoaderArray addObject:[getarray objectAtIndex:j]];
}
} // end of for
[pickerView reloadAllComponents];
[pickerView selectRow:0 inComponent:0 animated:NO];
}
-(NSInteger)numberOfComponentsInPickerView:(NSInteger)component
{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)picker numberOfRowsInComponent:(NSInteger)component
{
return [pickerLoaderArray count];
}
-(NSString *)pickerView:(UIPickerView *)picker titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return [pickerLoaderArray objectAtIndex:row];
}
The button:
- (IBAction)deleteButton:(id)sender {
NSUserDefaults *CheckFiltersUsed = [NSUserDefaults standardUserDefaults];
NSInteger myInt = [CheckFiltersUsed integerForKey:#"FiltersUsed"];
if (myInt<=20 && myInt>0) {
NSLog(#"number of filters used before deleting %ld",(long)myInt);
[CheckFiltersUsed setInteger:myInt-1 forKey:#"FiltersUsed"];
[CheckFiltersUsed synchronize];
// get names array
NSMutableArray *getarray = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"FilerNamesArray"]];
NSArray *get=getarray;
// at location where name matches with selectedfilter..put NULL
for (int j=0; j<20; j++) {
if ( [[getarray objectAtIndex:j] isEqualToString:_filterToDelete.text] && isFilterDeleted==NO )
{
NSLog(#"------currently %d is %#",j,[getarray objectAtIndex:j]);
[getarray insertObject:#"NULL" atIndex:j];
NSLog(#"------now %d is %#",j,[getarray objectAtIndex:j]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"" message: #"Deleted" delegate: nil cancelButtonTitle:#"Ok" otherButtonTitles:nil]; [alert show];
isFilterDeleted=YES;
[[NSUserDefaults standardUserDefaults] setObject:getarray forKey:#"FilerNamesArray"];
[[NSUserDefaults standardUserDefaults]synchronize];
[self loadthepicker];
}
else
{
NSLog(#"No matching filter name");
}
} // end of for
//now save this array back.
}
else
{
NSUserDefaults *CheckFiltersUsed = [NSUserDefaults standardUserDefaults];
NSInteger myInt = [CheckFiltersUsed integerForKey:#"FiltersUsed"];
NSLog(#"Wrong number of filters!!!... %d",myInt);
}
}
If i get what you are trying to do, you want to delete the equal string from the array and the picker as well. But instead of that you just insert another NSString object into index 'j'
In the deleteButton method:
Instead of this line
[getarray insertObject:#"NULL" atIndex:j];
Call
[getarray removeObjectAtIndex:j];
**Update
In the loadPicker just remove the if statment to check if the string is equal to #"NULL"
So instead of:
for (int j=0; j<20; j++) {
if ([[getarray objectAtIndex:j] isEqualToString:#"NULL"])
{
// do nothing..don't load
}
else // add that filter to pickerLoaderArray
{
[pickerLoaderArray addObject:[getarray objectAtIndex:j]];
}
}
Do:
for(NSString *pickerValue in getarray){
[pickerLoaderArray addObject:pickerValue];
}
I have an NSMutable array which i'm trying to remove objects from and by that to decrease
the count of number of objects on the array, and the count always stays the same,
Meaning, the remove is not working.
Here is the code (it's from the online stanford IOS development course):
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
- (void)addCard:(Card *)card
{
[self addCard:card atTop:NO];
}
- (Card *)drawRandomCard
{
Card* randomCard = Nil;
NSLog(#"This is the count %d",[self.cards count]);
if ([self.cards count]) {
unsigned index = arc4random() % [self.cards count];
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
The count is always 52, even after removing the objects.
Any ideas on how to fix this?
Its complicated to put in words but lemme give it a try. I have a MenuViewController that has an array with category names, tapping on the category rows in tableview instantiate a different view controller using the Storyboard ID.
Now if i use different classes for each view controller, that would be a lot of redundant code and classes. What i want to do is to use one class for all these view controllers lets call it PrimaryViewController and upon selecting different categories in the MenuViewController, it calls different methods or blocks in the PrimaryViewController.
Here is the method in the PrimaryViewController:
- (void) fetchData:(NSInteger )pageNumber
{
channel = [[TheFeedStore sharedStore] fetchWebService:pageNumber withCompletion:^(RSSChannel *obj, NSError *err){
if (!err) {
int currentItemCount = [[channel items] count];
channel = obj;
int newItemCount = [[channel items] count];
int itemDelta = newItemCount - currentItemCount;
if (itemDelta > 0) {
NSMutableArray *rows = [NSMutableArray array];
for (int i = 0; i < itemDelta; i++) {
NSIndexPath *ip = [NSIndexPath indexPathForRow:i inSection:0];
[rows addObject:ip];
}
[[self tableView] insertRowsAtIndexPaths:rows withRowAnimation:UITableViewRowAnimationBottom];
}
}
}];
}
The above code has the ability to load one category. Notice the first line "channel = [[TheFeedStore sharedStore] fetchWebService", the other categories are named "fetchWebServiceCat2", "fetchWebServiceCat3" and "fetchWebServiceCat4" in another class named TheFeedStore.
What i want is when a different view controller is instantiated from the MenuViewController, it should use PrimaryViewController's fetchData method to call a different category method of TheFeedStore.
Thanks!
[store fetchWebService:webService withCompletion:completion];
is equivalent to:
[store performSelector:#selector(fetchWebService:withCompletion:)
withObject:webService
withObject:completion];
So you can do this:
SEL sel = nil;
if (...) sel = #selector(fetchWebService:withCompletion:);
if (...) sel = #selector(fetchWebServiceCat2:withCompletion:);
...
[store performSelector:sel withObject:webService withObject:^{}];
Or even this:
SEL sel = NSSelectorFromString([NSString stringWithFormat:#"fetchWebService%#:withCompletion:", #"Cat2"]);
...