2

If you wanna see the code Im having problem with, here is the link: Code

My question is connected with my past question.

I'm really having problem with my NSMutableArray, I'm currently using iCarousel for my slotMachine object(slot1 and slot2). My app works this way:

From PhotoViewController I made a view that has thumbnail images, then assign its frame with button. So if 1 image was pressed, it will save that integer via NSUserDefaults. Then I will retrieve it in my carouselViewController

Im thinking of adjusting the array but I can't. I also have tried my question here: Comparing with NSMutableArray If only I can do it the same as Array 2 it would be much easy, but still not working.

(ADDITIONAL INFO:)

I have done it this way, have a Viewcontroller that contains the UIImageView with a button in it, so when the user taps it, my CustomPicker pops up. My CustomPicker contains the image on what the user have picked on the camera roll. So each button has a specific value sent to my iCarouselView using NSUserDefaults. carousel1 for First slot and carousel2 for Second slot.

Here is what I wanna do: I want to forcefully make it stop to the index the user picks. (Which Im doing in my carouselDidEndScrollingAnimtaion)

In my carouselDidEndScrollingAnimation method i tested all of my condition(individually) it works perfectly in terms of comparing. Then when I combine the conditions, the first Two comparison or STOP is RIGHT, but the next two are always wrong. Or sometimes Got mixed up.

I need to scroll the two specific indexes/integer which was User Picked( I already done that) was able to scroll 2 pairs of them but then the next two were always wrong because I think there indexes were adjusting.

PICTURES:

Image Below is my PhotoViewController which contained the Comparing Stage SETTING of my game.UIImageVIew with UIButton.Image that will be put in the number according to it will be Forcefully and should be forcefully shown.

enter image description here

When my iCarousel start then it stops for example in the image below(Which is not the same as the above): enter image description here

Will be forcefully scroll to the inputted image in the PhotoViewController

Into: enter image description here

Summary: Its like this. I have a settingsView from there, I will import my images(Multiple) for Slot1 & Slot2. Then in another View the PhotoViewController that is where the image above is shown. THe first column corresponds to 1st slot followed by the 2nd slot. if a view is pressed (for example No. 1 of Slot 1 it will load a thumbnail of images loading the images picked from Picker for the Slot 1.

You will have to do it 4 times(pair) ----> The displayed here I get their indexes via NSUserDefaults via button.tag then send to iCarouselView.

Then when you are done (pressed Done button) it will go to iCarouselView then, as shown above thats the view of it. When pressed it will spin for couple of seconds, then when finished but not stop at the user picked in the PhotoView it will forcefully scroll to that index.

QUESTION:

Is there a way to make my array or my iCarousel.view not adjust their indexes when Im deleting. To still retain my indexes the right way. Or are there other solution like adjusting my array, the same as adjusting my PhotoViewController picked indexes too. Because I think that when my array retain their indexes even deleting I would be able to solve this problem. But still can't.

Hope you understand my question.

Community
  • 1
  • 1
Bazinga
  • 2,456
  • 33
  • 76
  • can you share your code in simple project? – Volodymyr B. Jul 09 '12 at 08:37
  • @SAKrisT, do you want me to send it to you? – Bazinga Jul 09 '12 at 09:06
  • Could you explain what you are trying to do in `carouselDidEndScrollingAnimation`? what relation has this with deleting the items? – sergio Jul 09 '12 at 09:29
  • Not the solution but NSLog(@"choiceSlot1: %@", choiceSlot1); and the others must crash because you are trying to call description method on a BOOL. – Johnmph Jul 09 '12 at 09:52
  • what are then slot3 and slot4 for? you are scrolling the two carousel a few times in `carouselDidEndScrollingAnimation`, while you should scroll them just once... furthermore, you get two calls to `carouselDidEndScrollingAnimation` from the two carousels, so this is really puzzling... – sergio Jul 09 '12 at 09:54
  • @sergio Im trying to scroll the two Slots to the 2 user picked index. should it have two carouselDidEndScrollingAnimation> – Bazinga Jul 09 '12 at 09:56
  • @Johnmph, just ignore that. forgot to delete. – Bazinga Jul 09 '12 at 10:52
  • 3
    I still don't get what the real question is ... You should edit the whole question and make it simple and **clear**. – marzapower Jul 09 '12 at 12:31
  • Which part is unclear? Im really having a hard time explaining because its all connected @marzapower – Bazinga Jul 09 '12 at 13:19
  • Can you provide a question? This post is full of statements, but I cannot get what/where the problem is. – marzapower Jul 09 '12 at 13:21
  • I need to scroll the two specific indexes/integer which was User Picked( i already done that) was able to scroll 2 pairs but then the next two were always wrong because I think there indexes were adjusting. – Bazinga Jul 09 '12 at 13:26
  • 2
    I just read this, and I have no idea what you're actually asking. – Steven Fisher Jul 09 '12 at 14:37
  • @StevenFisher, this is really hard. I aint fluent in english. Sorry. I just wanted to scroll the two carousel according to specific index from a custom picker with my thumbnail image that was assign with button.tag. – Bazinga Jul 09 '12 at 14:50
  • Your English seems fine to me. :) It's the structure of the question that's making it difficult. – Almo Jul 09 '12 at 15:01
  • 1
    sorry, I still dont get it. Can you add a final simple question as sum-up? in the form of **How can I … ?** – vikingosegundo Jul 10 '12 at 13:01
  • @vikingosegundo, I have posted my question. – Bazinga Jul 11 '12 at 06:30
  • Everyone, have updated and edit it. Please check. Thanks – Bazinga Jul 11 '12 at 11:10
  • Not sure if I understand completely, but when one of the elements in your mutable array is deleted, rather than just deleting it, maybe insert it with another "dummy" place holder object? That way your indexes won't change at all when a delete occurs. – Maxwell Bohling Jul 13 '12 at 15:46

5 Answers5

1

Its a little difficult to know what the issue is here. Are you using a single NSMutableArray for the images and using the NSUserDefaults value to get the object at the index in the array?

Im not 100% sure on what is happening. What does the user do(and in what view) and what is triggered after that(which view is presented).

Are you trying to stop the "spinning" images on the image that is the same as the one picked from the previous view?

According to your images above, the images are off by a single index. Is this the case every time? Maybe there is an issue with your fetching from the array.

If you give me some more info I can help.

I looked through the code you pasted again and I think this might be your issue

if (twoSlot1 > [(UIImageView*)[self.carousel2 currentItemView] tag]){            
                [self.carousel1 scrollToItemAtIndex:(-twoSlot1)-2 duration: 3.5f];
            } else {
                [self.carousel1 scrollToItemAtIndex:-twoSlot1 duration: 3.5f];
            }

On all other code blocks like that you have this where you call each carousel. In the above code you call carousel 1 twice.

if (slot2 > [(UIImageView*)[self.carousel1 currentItemView] tag]){
            [self.carousel1 scrollToItemAtIndex:(-slot2)-2 duration: 3.0f];
            } else {
            [self.carousel1 scrollToItemAtIndex:-slot2 duration: 3.0f];    
            }

            if (twoSlot2 > [(UIImageView*)[self.carousel2 currentItemView] tag]){
            [self.carousel2 scrollToItemAtIndex:(-twoSlot2)-2 duration: 3.5f];    
            } else {
            [self.carousel2 scrollToItemAtIndex:-twoSlot2 duration: 3.5f];
            }

You call self.carousel1 when you should be calling number 2.

Is this correct?

some_id
  • 29,466
  • 62
  • 182
  • 304
  • Im using 2 NSMutableArray. And yes Im using the NSUserDefaults to get the objects index in the 2 array. Yes im trying to stop the spinning the same as the one picked from the previous view. For you're other question, I have a summary of it above. kindly look. – Bazinga Jul 10 '12 at 01:51
  • Hi. Now there seems to be 4 slots. Could you please help us all and explain What the issue is and where is might be occurring? Im not sure where you are having an issue. Do you even get to the compare code? – some_id Jul 10 '12 at 08:47
  • I edited my answer. looks like you are calling the carousel1 instead of 2 on the first if pair call – some_id Jul 10 '12 at 09:00
  • yes you are right, but my issue, is connected with adjusting my carousel indexes – Bazinga Jul 10 '12 at 09:08
  • I would just set breakpoints at the key function calls and observe the object data in the debugger. Will be faster than this. Check the indexes and image links etc at each breakpoint when the debugger pauses execution. – some_id Jul 10 '12 at 09:12
  • I think the real issue is in my condition in `carouselDidEndScrolling`. – Bazinga Jul 10 '12 at 10:33
  • What about the issue I mentioned before. You were setting the wrong variable. Doesnt this solve it? – some_id Jul 10 '12 at 10:37
  • So, setting a different variable causes no changes? weird. whats the point of it then? – some_id Jul 10 '12 at 11:15
  • I have another code the same as those but still doing it wrong. I think the way im adjusting the indexes. I really dont know – Bazinga Jul 10 '12 at 12:48
  • I would love to help, but the question is unclear. I dont know exactly what the issue is. :( – some_id Jul 10 '12 at 12:58
  • Im sorry, but your english is really hard to follow. I tried but I still dont get it. What are you trying to do? if you want the same image, why use more than one array? If you dont want to delete items from the array, then flag the items instead and do an if (flagSet) check or something. I still dont get exactly what the point of this is. Where you explain that if the image didnt stop on the photo they selected it will be forced to stop at the index from NSUserDefaults. Why cant you use a single array for both slots? I dont get it, sorry. – some_id Jul 12 '12 at 00:55
  • In only using two arrays, 1 for the slot1 and another for the slot2. I dont know how to flag the items. Its like my comparing stage, i want to scroll to the image/index the user wants too, so that's why I cant use the same array in both slots. – Bazinga Jul 12 '12 at 01:44
  • This just seems to go on forever. I still have no clue what the issue is. If you have 2 arrays, and use an NSUserDefaults stored integer to get an objectAtIndex, what is the problem? Did you print out the contents of the array or set breakpoints at those trigger points and observe what value you are getting from NSUserDefaults and what is at that index in both arrays? – some_id Jul 12 '12 at 09:53
  • the indexes I'm getting are right, even my conditions individually, but when it goes to deleting indexes, that's where my scrolling became wrong. – Bazinga Jul 12 '12 at 09:56
  • So, what do you need to delete (same index in both arrays)? and what method are you calling to delete indexes? – some_id Jul 12 '12 at 13:08
  • I think sergio got it, cause he explained it, please see. – Bazinga Jul 13 '12 at 01:22
1

Is there a way to make my array or my iCarousel.view not adjust their indexes when Im deleting. To still retain my indexes the right way. Or are there other solution like adjusting my array, the same as adjusting my PhotoViewController picked indexes too. Because I think that when my array retain their indexes even deleting I would be able to solve this problem. But still can't.

The only way you have to modify the way iCarousel manages its indexes is by modifying the code. Indeed, if you look at the removeViewAtIndex method in iCarousel.m, you will see that indexes are managed through an NSDictionary, and at the moment of deleting, the dictionary is rearranged (items are reordered). You could take that method:

- (void)removeViewAtIndex:(NSInteger)index
{
    NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[itemViews count] - 1];
    for (NSNumber *number in [self indexesForVisibleItems])
    {
        NSInteger i = [number integerValue];
        if (i < index)
        {
            [newItemViews setObject:[itemViews objectForKey:number] forKey:number];
        }
        else if (i > index)
        {
            [newItemViews setObject:[itemViews objectForKey:number] forKey:[NSNumber numberWithInteger:i - 1]];
        }
    }
    self.itemViews = newItemViews;
}

You could apply the same logic to your array, so that the carousel and your array keep in sync. Of course, if you store the indexes somewhere (slot1/slot2/slot2/slot4?), you should also update their values after removing an element.

On the other hand, I think that what you are asking here is how to do something that you believe would solve the problem you have, but you are not really explaining what the problem is. Indeed, if I understand you correctly, what you do is:

  1. spinning the carousel;

  2. when the carousel stops, if it is not by chance on the desired item, you "force" it to scroll to that item.

There is no reason why this should not work after deleting some elements (unless iCarousel has some bugs, then the solution would be catching the bug). The only part is knowing which index is the one you would like to move to.

As a suggestion, I would start off by simplifying your delegate carouselDidEndScrollingAnimation method. Indeed, your carouselDidEndScrollingAnimation has a parameter called carousel, well, I think this is the only carousel you should ever be referring to in that method. If you don't see it, this is the reasoning: each of your carousel will stop scrolling and the carouselDidEndScrollingAnimation will be called; so that method will be called twice. Each time that method is executed you will modify the state of both carousel1 and carousel2 (by calling scrollToItemAtIndex); therefore, on each carousel you will call scrollToItemAtIndex twice.

This does no sound very correct to me. So you should find a way to scroll only carousel1 when carouselDidEndScrollingAnimation is called for carousel1 and to scroll only carousel2 when carouselDidEndScrollingAnimation is called for carousel2.

More generally, another point I would like to raise is that the idea of:

  1. letting a carousel stop;

  2. scrolling it again so that it reaches the desired position;

does not seem the best implementation possible since the user would see the carousel stopping and then starting over again.

The way I would approach this is by modifying directly iCarousel implementation so that it supports this specific behavior you need.

Concretely, give a look at the step method in iCarousel.m. This is called at each frame to produce the carousel animation. Now, in this method there is decelerating branch:

else if (decelerating)
{
    CGFloat time = fminf(scrollDuration, currentTime - startTime);
    CGFloat acceleration = -startVelocity/scrollDuration;
    CGFloat distance = startVelocity * time + 0.5f * acceleration * powf(time, 2.0f);
    scrollOffset = startOffset + distance;

    [self didScroll];
    if (time == (CGFloat)scrollDuration)
    {
        decelerating = NO;
        if ([delegate respondsToSelector:@selector(carouselDidEndDecelerating:)])
        {
            [delegate carouselDidEndDecelerating:self];
        }
        if (scrollToItemBoundary || (scrollOffset - [self clampedOffset:scrollOffset]) != 0.0f)
        {
            if (fabsf(scrollOffset/itemWidth - self.currentItemIndex) < 0.01f)
            {
                //call scroll to trigger events for legacy support reasons
                //even though technically we don't need to scroll at all
                [self scrollToItemAtIndex:self.currentItemIndex duration:0.01];
            }
            else
            {
                [self scrollToItemAtIndex:self.currentItemIndex animated:YES];
            }
        }
        else
        {
            CGFloat difference = (CGFloat)self.currentItemIndex - scrollOffset/itemWidth;
            if (difference > 0.5)
            {
                difference = difference - 1.0f;
            }
            else if (difference < -0.5)
            {
                difference = 1.0 + difference;
            }
            toggleTime = currentTime - MAX_TOGGLE_DURATION * fabsf(difference);
            toggle = fmaxf(-1.0f, fminf(1.0f, -difference));
        }
    }
}

and you see that when the carousel stops decelerating, it is scrolled again. This is exactly the same as you are doing, so you might find a way to modify this code and have the carousel scrolls exactly to the index you need. In this way you would get a far smoother spinning of the carousel.

Hope this helps and apologies for the lengthy reply.

sergio
  • 68,819
  • 11
  • 102
  • 123
  • I think you are right with the `removeViewAtIndex`. maybe I can just use that method, instead of the other one? Im really am having problem with my indexes – Bazinga Jul 13 '12 at 02:15
  • I tried using it [carousel removeViewAtIndex] if I can call it in my view that would be my answer. please help me sir. – Bazinga Jul 13 '12 at 02:26
  • You can undoubtably call `removeViewAtIndex`, but I doubt it will help; it will only put the carousel in an inconsistent state and make it crash. What you need to do is fix the way you manage your indexes -- and you can try follow the same path as in `removeViewAtIndex` - this is what I meant. You cannot: 1) store an index; 2) delete an element; 3) try to use the stored index, e.g. -- but in the end it is not clear what you are doing with your indexes, so I cannot say what is your problem... – sergio Jul 13 '12 at 06:17
  • above all, if you allow me a bit of frankness, your code has several problems that *all* concur in producing the wrong results, as I have outlined above. You should go about fixing them one by one, and only at the end you will see that it works (maybe, if you are lucky enough). You will not find, I think, a single/simple magical touch that will fix it on the whole. Revise your design and try to find the correct way to do things (as I said, I gave you my 2 cents in my answer). – sergio Jul 13 '12 at 06:18
  • Sir,I was able to make it work with only one carousel, but when there are two carousel, it became wrong at some parts. I know my condition are wrong but Ive been figuring this a bit long by now. it really hard managing those indexes. – Bazinga Jul 13 '12 at 06:35
  • `NSArray *visibleItemViews` `NSArray *indexesForVisibleItems` I have found these included in `iCarousel` could it also solve my problem? – Bazinga Jul 13 '12 at 07:01
1

Referring to your question. You want an array that does not change its members' indexes when a member is deleted from the array.

I guess you could use an NSMutableDictionary. It is an associative array so to say, where the indexes are of your choice and they remain unchanged when you delete a member from in between. You may still use 0..n as your Index. You can still use some methods that you are familiar with from NSArray, such as count. You can use an enumerator to go through all members of the dictionary. On the other hand you can still use your for-loops as you are used to use them with arrays. Just be prepared that a) objectForKey:i may return nil if the key/index does not exist (e.g. was deleted) and that count retuns the number of the objects but not the highest index+1 as it does with arrays.

Hermann Klecker
  • 14,039
  • 5
  • 48
  • 71
0

I'm having a hard time understanding your overall problem, but from what I can gather the crux of your question is this:

Is there a way to make my array or my iCarousel.view not adjust their indexes when Im deleting.

I don't know whether it will solve your bigger issue, but using an NSMutableDictionary to simulate an array should allow you to do this. You can simply use the indices as the keys to the dictionary, and then when you remove the item associated with an index, no other indices will be adjusted as a result. For example:

NSMutableDictionary *arrayDict = [[NSMutableDictionary alloc] init];
[arrayDict setValue:foo forKey:[NSNumber numberWithInt:[arrayDict count]]];
[arrayDict setValue:bar forKey:[NSNumber numberWithInt:[arrayDict count]]];
[arrayDict setValue:fooBar forKey:[NSNumber numberWithInt:[arrayDict count]]];

And then you can access the object an at index with [arrayDict objectForKey:[NSNumber numberWithInt:index]].

Note that using an NSNumber for the key parameter of setValue:forKey: will generate a warning, but you can safely ignore this (or use the string representation if it bothers you).

Ethan Holshouser
  • 1,402
  • 14
  • 13
  • I am talking about completely replacing the array with a dictionary, not just adding a dictionary to the array. – Ethan Holshouser Jul 13 '12 at 07:05
  • You can simulate an array by storing the indices of the array as the keys of the dictionary. I will edit the answer to show an example of how this might be done. – Ethan Holshouser Jul 13 '12 at 17:24
  • How in my code? So you mean im going to assign my array indexes in an nsmutabledictionary then call that whenever im deleting? – Bazinga Jul 14 '12 at 13:59
  • 1
    Treat it exactly like you would treat a mutable array. Anywhere you would call `removeObjectAtIndex:`, you instead call `removeObjectForKey:`. – Ethan Holshouser Jul 14 '12 at 19:00
  • This doesnt solve the issue but still give you credit. I was abke to figure it out. – Bazinga Jul 22 '12 at 03:42
0

Not sure if I understand completely, but when one of the elements in your mutable array is deleted, rather than just deleting it, maybe insert it with another "dummy" place holder object? That way your indexes won't change at all when a delete occurs

  • For example if I have a NSMutableArray of objects 1,2,3, there is a method called deleteObject:atIndex and insertObject:atIndex and I can use this to delete object 2 and replace with a -1. So the -1 is a place holder in the mutable array avoiding the indices being changed at all. – Maxwell Bohling Jul 13 '12 at 15:59
  • what do you mean by dummy place holder? wont it crash? I tried inserting a NULL. but crashes – Bazinga Jul 13 '12 at 16:16
  • Try anything but null, add some fake object. It has to be of type (id). (Meaning anything but null). – Maxwell Bohling Jul 13 '12 at 16:22
  • Okay so lets say I have an array of NSStrings, NSMutableArray *stringsArray = [[NSMutableArray alloc] initWithObjects:@"String1", @"String2", @"String3", nil]; Now say I want to delete "String2" and replace with a placeholder string object. I would say [stringsArray deleteObjectAtIndex:1]; [stringsArray insertObject: @"PlaceHolderString"]; Now the array holds @"String1", @"PlaceHolderString", @"String3", in that order. – Maxwell Bohling Jul 13 '12 at 16:28
  • Okay so you have this line: [carousel1 removeItemAtIndex:index animated:YES]; in your -(void)deleteItems method. So add the line [carousel1 insertItem:(A placeholder object) atIndex:index]; and do the same for index2. – Maxwell Bohling Jul 13 '12 at 17:42
  • I can't be too sure of what objects your array is actually holding so I can't give you an example of a placeholder object but I hope you get the point. – Maxwell Bohling Jul 13 '12 at 17:45
  • I think its view is scrollview – Bazinga Jul 14 '12 at 13:57