what is wrong with my loop - looping through views - objective-c

Could someone point out what is wrong with my for loop? I'm trying to loop thru UIImageViews to make all hidden. All the subviews are UIImageViews. Thanks in advance.
int i;
int num = [[self myView]subviews];
for (i=0; i<num; i++)
{
UIImageView *currentView = [self.tabber.subviews objectAtIndex:i];
currentView.hidden = YES;
}

Ian, Martin and Aram pointed out the error in your code.
I want to point out another way of iterating through an array: Fast Enumeration
for (UIView *view in [self.myView subviews])
{
view.hidden = YES;
}
It has several advantages over C-style counting variable based iterating, such as
cleaner code — no counter variable needed.
Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.

[[self myView] subviews] refers to the list of subviews, not the number of them. What you want is
for(i = 0; i < [[[self myView] subviews] count]; i++)

A UIView's subview property returns an NSArray.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html

[[self myView]subviews]; returns a NSArray of views, not an int.

Related

UICollectionView Very Slow Custom Layout

I am using a UICollectionView with a custom layout that lays out cells in a grid format. There can be well over 50 rows and 50 columns. Scrolling occurs both vertically and horizontally. Currently, I am doing all of the layout setup in prepareLayout and storing it in arrays:
- (void)prepareLayout {
NSMutableArray *newLayoutInfo = [[NSMutableArray alloc] init];
NSMutableArray *newLinearLayoutInfor = [[NSMutableArray alloc] init];
NSInteger sectionCount = [self.collectionView numberOfSections];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
self.heightForRows = [delegate collectionViewHeightForAllRows];
self.totalWidthsForRows = [[NSMutableArray alloc] init];
for (int i = 0; i < sectionCount; i++) {
[self.totalWidthsForRows addObject:[NSNumber numberWithInt:0]];
}
for (NSInteger section = 0; section < sectionCount; section++) {
NSMutableArray *cellLayoutInfo = [[NSMutableArray alloc] init];
NSInteger itemCount = [self.collectionView numberOfItemsInSection:section];
for (NSInteger item = 0; item < itemCount; item++) {
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
itemAttributes.frame = [self frameForCellAtIndexPath:indexPath];
[cellLayoutInfo addObject:itemAttributes];
[newLinearLayoutInfor addObject:itemAttributes];
}
[newLayoutInfo addObject:cellLayoutInfo];
}
self.layoutInfo = newLayoutInfo;
self.linearLayoutInfo = newLinearLayoutInfor;
}
Then in layoutAttributesForElementsInRect I have:
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *rows = [self.linearLayoutInfo filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *evaluatedObject, NSDictionary *bindings) {
return CGRectIntersectsRect(rect, [evaluatedObject frame]);
}]];
This works okay, but it is laggy and jumpy when I have over 50 columns and 50 rows. The problem I now have is that I must set
-(BOOL)shouldInvalidateLayoutForBoundsChange {
return YES;
}
This makes it prepare the entire layout every time the bounds change, which, needless to say, has a huge impact on performance and you can barely scroll. The cells consist of just text with an opaque background, so there is no issue there.
I am sure I am not doing this right and that there must be a better way. Thanks for the help in advance.
In custom flow layout I do this and it seems to help:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return !(CGSizeEqualToSize(newBounds.size, self.collectionView.frame.size));
}
-(BOOL)shouldInvalidateLayoutForBoundsChange {
return YES;
}
Causes the layout to do prepareLayout() every time it scrolls, which means anything of heavy computing in prepare will lead to a laggy practice, so one possible direction to solve this is to check what's really taking much time. One possibility is what's inside
for (NSInteger section = 0; section < sectionCount; section++)
{
// generate attributes ...
}
in order to generate attributes for the layout. Every time it scrolls, every time this generalization reruns, so that it impacts on the scroll appear to be jumpy and clumsy. So in order to solve this issue, or at least sort out that this is not the really trouble, I suggest setting a flag in this layout algorithm, say, isScrolling, standing for the situation where the layout needs to prepare. Every time in prepareLayout() check the flag, if it is YES, then we'll know there's no need to do for loop to regenerate all the attributes, which alreay exsit ever since the first time the layout is initialised.
ok--I understand now. Here's what I recommend: create 3 collection views... one for the column headers (where each cell is column header), one for the row leaders (each cell = 1 row leader) and one collection view for your cells. Then when the scroll position of any collection view is changed by the user, update the scroll positions for the other 2 collection views as appropriate.

Removing object at index NSMutableArray [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Objective-C NSMutableArray mutated while being enumerated?
I use this code to remove an object at index:
-(IBAction)deleteMessage:(id)sender{
UIButton *button = (UIButton*) sender;
for (UIImageView *imageView in imageArray)
{
if ([imageView isKindOfClass:[UIImageView class]] && imageView.tag == button.tag)
{
if (imageView.frame.size.height == 60) {
x = 60;
}
if (imageView.frame.size.height == 200) {
x = 200;
}
for (UITextView *text in messagetext)
{
for (UITextView *name in messagename)
{
if ([text isKindOfClass:[UITextView class]] && text.tag == button.tag && text.tag== name.tag)
{
[imageView removeFromSuperview];
[messagename removeObjectAtIndex:button.tag - 1];
[messagetext removeObjectAtIndex:button.tag - 1];
}
}
}
The error is:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x704bdb0> was mutated while being enumerated.'
I noticed though that if I delete first the last object in the array, and go in order from last to firs, it works. But if I try removing an object at an index that is not the last, the app crashes and gives the error: (1,2,3,4..I delete object2... crash...if I delete object 4 no crash)
One way to do this is to make an array with the indexes you intend to remove, the you do your loop, add the indexes and remove the objects afterwards. Something like this:
NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
// Inside your loop
[indexes addIndex:(button.tag - 1)];
//..
// After your loop
[messagename removeObjectsAtIndexes:indexes];
[messagetext removeObjectsAtIndexes:indexes];
Now if you want different indexes for both arrays just make another NSMutableIndexSet and add the second set of indexes to it. Also don't forget to release indexes if you don't use ARC.
You can't use "for each" style iteration on an array if you plan to mutate it (i.e., remove an element) because it messes with the iteration. If you really want to remove an element from the array as you iterate it, you need to use the "old style" iteration. Another Stack Overflow post here does a good job of showing how to use the old style that will allow you to mutate the array.
You can't use "for x in y" iteration on an array if you insert or remove objects from it.
Either you have to use a good ol' fashioned array or you could keep a reference to the object you want to remove and then remove it afterwards:
NSObject *messageNameToRemove;
NSObject *messageTextToRemove;
for (UITextView *text in messagetext)
{
for (UITextView *name in messagename)
{
if ([text isKindOfClass:[UITextView class]] && text.tag == button.tag && text.tag== name.tag)
{
[imageView removeFromSuperview];
messageNameToRemove = [messagename objectAtIndex:button.tag -1];
messageTextToRemove = [messagetext objectAtIndex:button.tag -1];
}
}
[messagename removeObject:messageNameToRemove];
[messagetext removeObject:messageTextToRemove];

Most efficient way to draw lines from array of points in cocos2d

Given an NSMutableArray of dynamic CGPoints, what is the fastest and most efficient way to draw lines from array[0] to array[1], array[1] to array[2], etc.? Should I rewrite my function in C or C++ for better performance? Currently my framerate suffers dramatically when there are more than ~20 points in the array. I am using cocos2d v2.0.0-rc2 and I currently have:
-(void)draw
{
for (int i = 0; i < [points count]; i+=2)
{
CGPoint startFromArray = [[points objectAtIndex:i] CGPointValue];
CGPoint endFromArray = [[points objectAtIndex:i+1] CGPointValue];
ccDrawLine(startFromArray, endFromArray);
}
[super draw];
}
There's no need to use iteration here. Cocos2d has a built-in function called ccDrawPoly(). You can use it like this:
CGPoint *verts = malloc(sizeof(CGPoint) * [points count]);
for (int i = 0; i < [points count]; i++) {
verts[i] = [[points objectAtIndex:i] CGPointValue];
}
ccDrawPoly(verts, [points count], NO);
free(verts);
Obviously, you'll get even better performance if you store your CGPoints in a C array instead of boxing and unboxing them from NSValues, but if you really need the mutability, that can't be helped.
As for the third argument of ccDrawPoly(), setting it to YES will connect the start and end points of the array, making a closed polygon, while using NO will just make a bunch of open lines.

MKMapView: show or hide an array of annotations without looping

I have an array of annotations.
NSArray *annotations = [mapView annotations];
I can show or hide them by looping through the array.
for (i=0; i<[annotations count]; i++)
{
annotation = (AddressAnnotation*)[annotations objectAtIndex:i];
[[mapView viewForAnnotation:annotation] setHidden:YES];
}
But is there any way to do that without looping?
We can add annotations by the method
- (void)addAnnotations:(NSArray *)annotations;
Also we can remove the annotations by like below:
- (void)removeAnnotations:(NSArray *)annotations;
But i cant find out any method to show or hide an array of annotations :(
not sure if you solved this, but in MapKit you can use
NSArray *annotationsOnMap = mapView.annotations;
if ([annotationsOnMap count] > 0) {
[mapView removeAnnotations:annotationsOnMap];
}

IKImageBrowserView Moving items doesn't animate

I have an IKImageBrowser setup which appears to be working well. I have set it up to allow reordering and also set animation to YES (in my awakeFromNib), however whenever I select and try and reorder the images I get strange behaviour:
1) They don't reorder most of the time
2) If they do they don't animate
3) Sometimes the images land on each other, if I scroll away and back they are back where they started.
If I highlight an image and delete it, it animates and disappears as expected...
Is this a problem with Core Animation? Do I need to add a core animation layer to the object in interface builder? I followed this tutorial from Apple to get these results: http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/ImageKitProgrammingGuide/ImageBrowser/ImageBrowser.html#//apple_ref/doc/uid/TP40004907-CH5-SW1
Here's the code in question:
- (BOOL) imageBrowser:(IKImageBrowserView *) aBrowser moveItemsAtIndexes: (NSIndexSet *)indexes toIndex:(NSUInteger)destinationIndex{
int index;
NSMutableArray *temporaryArray;
temporaryArray = [[[NSMutableArray alloc] init] autorelease];
for(index=[indexes lastIndex]; index != NSNotFound;
index = [indexes indexLessThanIndex:index])
{
if (index < destinationIndex)
destinationIndex --;
id obj = [mImages objectAtIndex:index];
[temporaryArray addObject:obj];
[mImages removeObjectAtIndex:index];
}
// Insert at the new destination
int n = [temporaryArray count];
for(index=0; index < n; index++){
[mImages insertObject:[temporaryArray objectAtIndex:index]
atIndex:destinationIndex];
}
return YES;
}
Interestingly, this line throws a warning
for(index=[indexes lastIndex]; index != NSNotFound;
comparison is always true due to
limited range of data type
As mentioned above by NSGod, changing int index to NSUInteger index solved the problem