IOS: fill a NSArray with UIImageView - objective-c

In my project I created 30 small UIImageView where inside I put a different background. This is my group of UIImageView without background
and this is the group of UIImageView with different background:
I put these group of UImageView inside a UITableViewCell and I use them to define a timeline, but it's not important.
I declared in .h these UIImageView but now I must insert theme in an NSArray, or NSMutableArray? Because in my code (depending on the case) I must set different background for each UIImageView. How can I organize them in my code?

You don't want to put them all in an array. What you actually want to do is assign 'tags' to each view. Then, you can use -[UIView viewWithTag:(int)]; to get a pointer to the image view you want to 'talk' to at the time. Keep in mind not to use negative numbers or 0 when tagging your views.

Although I would suggest drawing your timeline view using a single UIView, dynamically creating and storing views in an array is simple:
NSMutableArray *views = [NSMutableArray array];
NSUInteger viewCount = 30;
NSUInteger index;
for (index = 0; index < viewCount; index++) {
UIImageView *newView = [[[UIImageView alloc] initWithImage:anImage] autorelease];
[newView setFrame:CGRectMake(index * width, 0.0, width, height);
[[self someView] addSubview:newView];
[views addObject:newView];
}
Remember to retain or copy views at some point.
Then, to modify a view:
[[views objectAtIndex:8] setImage:anotherImage];

Related

Programmatically drawn UIView tags (or something alike)

I'm drawing a view that contains an xib, multiple times (same uiview) and then update the outlets each time it gets drawn displaying an nsmutablearray with different strings on the outlets
Clicking the view opens a viewcontroller that should be displaying the data from the view (+more), this works perfectly fine except it doesn't know which index of the array it's supposed to be displaying.
I'm trying to figure out a way to manage them in a way that I can see which view is being pressed so I can pass that 'id' onto the new vc (something to uniquely identify the view even though it's the same view being drawn)
Here's how the view is being drawn multiple times
- (void) populateUpcoming:(int)events {
[self resetVariables];
upcomingEventsCenterPos = self.view.frame.size.width / 2 - 159;
for (int i = 0; i < events; i++) {
upcomingEventsY2 = 175 * upcomingEvents2;
UIView *firstViewUIView = [[[NSBundle mainBundle] loadNibNamed:#"UpcomingEventFull" owner:self options:nil] firstObject];
[_scrollView addSubview:firstViewUIView];
CGRect frame = firstViewUIView.frame;
frame.origin.y = 9 + upcomingEventsY2;
frame.origin.x = upcomingEventsCenterPos + upcomingEventsX2;
firstViewUIView.frame= frame;
upcomingEvents2++;
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tapUpcomingEvent:)];
[firstViewUIView addGestureRecognizer:singleFingerTap];
}
[self setupScroll];
}
I think you can use UITableView and custom UITableViewCell for this purpose. On click of "Add More", add one entry to NSMutableArray and just reload tableview.
And no need to manage index separately, you can get it directly by indexPath.row
Also you don't need to manage scroll view size as tableview automatically adjust is content height according to row.
If you are using same kind of view,then go with this approach, as its easy and will save your time.

Really simple xcode array issue

I'm programming a quiz app in xcode and I have 10 images which I want to use as UIImage views. I want to know how to create an array of all those images.
I also have a switch statement where I want to call 1 image from that array to be the UIImage. If you could explain how to call an image from an array as well I would be very grateful :)
Thanks in advance !!
Instead of storing UIImages in the array you can also store image names in array
NSArray *imagesArray = #[#"<image-name-1>",#"<image-name-2>",...,<image-name-10>];
when you want to get the image to set to UIImageView
UIImageView *imageView = [[UIImageView alloc] initwithFrame:<your-frame>];
[imageView setImage:[UIImage imageNamed:imagesArray[<your-index>]];
Let me know if you need any more info.
If you need to call an object from an array, objectAtIndex: can do the job for you. It doesn't matter whether you store UIImage instances of UIImageView instances, this will work with any form of object.
Here's a little example that will call each item from the array using objectAtIndex::
NSArray *_imageViews = ...
for (int idx; idx < _imageViews.count; idx++)
{
UIImageView *_imageView = [_imageViews objectAtIndex:idx];
/* do whatever you have to do with the image view */
}

Customizing tableView: Corner radius, decreased width and shadow

This is what I want to do:
As you can see i want to:
Decrease the width of the tableView (I want more margin on the sides than the grouped tableView provides)
Corner radius (bigger radius than the default for grouped tableView)
Drop shadow around the table and a special shadow beneath the last cell
You can do this by "drawing" the backgroundView of the cells yourself.
I'd recommend getting an image to use as the background (if the cells are all the same height).
You'll need three images.
A "top" image with the top corners rounded.
A "bottom" image with the bottom corners rounded and the drop shadow how you want it.
And a "middle" image with no rounded corners.
If the cells don't have any texture or gradient within them then you can use stretchable images to reduce the memory footprint of them.
Then I would subclass the UITableViewCell and override the backgroundView to add a UIImageView. I'd also provide an accessor method to change the type (top, middle, bottom) of the cell.
Each cell can then have three placeHolder properties of a UIImage (topImage, bottomImage and middleImage). When the type of the cell is changed these can be accessed (use lazy instantiation to make sure they are only loaded once and only when needed) and then set the backgroundVIew image to be the required image.
Something like this...
In the UITableViewCell subclass define a type enum...
typedef enum {
CellTypeTop,
CellTypeMiddle,
CellTypeBottom
} cellType;
Then a property for the type...
#property (nonatomic) cellType cellType
Then in the .m ...
Define some more internal properties...
#property UIImageView *bgImageView;
#property UIImage *topImage;
#property UIImage *middleImage;
#property UIImage *bottomImage;
Then add the imageView (only once)...
- (void)awakeFromNib //or in the init depends how you are initialising the cell
{
self.bgImageView = [[UIImageView alloc] initWithFrame:blah];
[self.backgroundView addSubView:self.bgImageView];
}
Now when the type is changed...
- (void)setCellType:(cellType)cellType
{
switch(cellType) {
case CellTypeTop:
self.bgImageView.image = self.topImage;
break;
case CellTypeMiddle:
self.bgImageView.image = self.middleImage;
break;
case CellTypeBottom:
self.bgImageView.image = self.bottomImage;
break;
}
}
Finally a lazy instantiation of the images...
- (UIImage *)topImage
{
if (_topImage == nil) {
_topImage = [UIImage imageNamed:#"topImage"];
//alternatively...
_topImage = [[UIImage imageNamed:#"topImage"] stretchableImageWith...
}
return _topImage;
}
Now repeat these for the other images.
This will be more performant (by a long way) than using a CALayer alternative and, especially if using the stretchable images, will have a very small memory footprint.
Several other users have said that this is not good for performance, memory, design, whatever, but it really is the best way to get the best performance for UserExperience than CALayers. Yes, it will use more memory than CALayers but only marginally and it will get to a limit as there are only a few dequeueable cells created.
A couple of links explaining performance issues when using CALayers in scrollViews...
http://www.quora.com/iOS-Development/What-is-the-best-way-to-optimize-the-performance-of-a-non-paging-but-view-recycling-UIScrollView-involving-loading-potentially-caching-and-displaying-bundled-images
Bad performance on scroll view loaded with 3 view controllers, drawn with CALayer
::EDIT:: Edit to answer Michael's question.
In the storyboard create a UITableViewController (rename the Class in the inspector so that it matches your subclass UITableViewController - I'll call it MyTableViewController).
Create a subclass of UITableViewCell (I'll call mine MyTableViewCell) in the code (i.e. the .h and .m).
Add the above code to do with properties and types and imageViews to your MyTableViewCell.h file.
In the storyboard select the cell in the TableViewController and rename the class to MyTableViewCell. Also set the reuse identifier on it.
In the MyTableViewController code you will need a function like this...
-(UITableViewCell*)tableView:(UITabelView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
MyTableViewCell *cell = [tableView dequeueCellWithReuseIdentifier:#"Cell"];
cell.cellType = CellTypeTop; //or whichever it needs to be
cell.textLabel.text = #"Blah";
return cell;
}
Oh, another thing, in the storyboard you will be able to layout your cell how you want it to look and link up all the labels and imageviews etc... Make sure you add IBOutlet to the UIImageView so that you can link it up in the storyboard.
make sure you have #import <QuartzCore/QuartzCore.h> imported, then you can start accessing the layers of the UITableView like.
UITableView *yourTable = [[UITableView alloc] initWithStyle:UITableViewStyleGrouped];
[[yourTable layer] setCornerRadius:10.0f];
[[yourTable layer] setShadowColor:[[UIColor blackColor] CGColor]];
[[yourTable layer] setShadowOffset:CGSizeMake([CALayer ShadowOffSetWidthWithFloat:10.0f], [CALayer ShadowOffSetWidthWithFloat:10.0f])];
[[yourTable layer] setShadowOpacity:[CALayer ShadowOpacity:1]];
[[yourTable layer] setMasksToBounds:NO];
UIBezierPath *path = [UIBezierPAth bezierPathWithRect:yourTable.bounds];
[[yourTable layer] setShadowPath:[path CGPath]];
This will add shadow affect to your table view with the shadow not masked to the bounds of the UITableView, at setCornerRadius you can set the corners of the table to whatever you want. You can also set the frame of the UITableView by doing
[yourTable setFrame:CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)];
EDIT
As another user has tried to point out that CALayer is very slow, this is not the case
CALayer was introduced to help performance issues around animation. Please read documentation. Loading an image straight in may seem like a good idea but in the long run will take up more memory. Please this question about memory allocation for images. As you can see it may seem faster, but it takes up 2.25 MByte of memory per image which after loading each image so many times your app will start to become slow.

How to refresh view in objective-c

I wanted to create a gallery. It loads different images based on the category, that a user selects. I used to populate images in UIImageViews.
My when selecting different categories is that it does not clear the previously selected images. This is my code for populating images.
-(void)refresh{
categoryLabel.text = treatment.treatmentName;
NSMutableArray *galleryImages =[NSMutableArray alloc] ;
galleryImages = [[PatientImage alloc]find:treatment.treatmentId];
int imgCount = [galleryImages count];
for(int i=0;i<imgCount;i++){
PatientImage *apatientImage = [galleryImages objectAtIndex:i];
UIImage *img1 = [UIImage imageNamed:apatientImage.imageBefore];
UIImageView *myImageView = [[UIImageView alloc] initWithImage:img1];
myImageView.contentMode = UIViewContentModeTopRight;
myImageView.frame = CGRectMake(120+i*240,120.0,100.0, 100.0);
[self.view addSubview:myImageView];
UIImage *img2 = [UIImage imageNamed:apatientImage.imageAfter];
UIImageView *myImageView2 = [[UIImageView alloc] initWithImage:img2];
myImageView2.contentMode = UIViewContentModeTopRight;
myImageView2.frame = CGRectMake(120+i*240+300,120.0,100.0, 100.0);
[self.view addSubview:myImageView2];
}
}
First things first, You have some memory leaks there. You are allocating UIImageViews but are not releasing them anywhere, after you have added them to your view. I don't know if that applies to ARC, though. Same applies to your Mutable array, but I suppose you are releasing it after the 'for' loop somewhere, since it seems you posted code after omitting some of it.
As far as your actual question is concerned, I wouldn't do this this way. I would make the mutable array an object variable, and then fill it with my image views. When calling refresh again, I would first call -removeFromSuperview on each image view, then empty the array, then repopulate it and add the new subviews to my view. That is the simple way.
I don't know if you are using ARC, but you should be careful about memory management when using dynamically loaded views. Each time you add a view to another one, you increase its retain counter. You must then call release to remove ownership, and let the iOS runtime handle the rest.
Also note that operations such as this using views are expensive in terms of memory. So, another way of repopulating the gallery view is to just change the image an imageView holds. That will save you some memory, and time. In case the view doesn't have a constant number of images to be displayed, you can refine your algorithm to change the images on the already created image views, and then add more image views if necessary or delete the remaining ones, if any.
I hope I helped.
try at the start of refresh call
[[self subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
or
for (id imageView in self.subviews){
if([imageView isKindOfClass:[UIImageView class]]) [imageView removeFromSuperview];
}
call [tableview reloadData] if You are using tableview to show your gallery images
or call view's
[self.view setNeedsDisplay] method for refreshing the view.

Variable that refers to many uiimageviews

Okay so what I want to do is I have an app with many uiimageviews that you can move and do actions with them. Instead of creating many outlets for each, how can i create one variable that refers to all these outlets (not IBOutletCollection because i would have to use array and arrays dont have properties that uiimageviews do. Ive tried that if youre positive it works please show me the code). For example I have many uiimageviews, variable = all my uiimageviews
So then [variable move]; it moves all the uiimageviews. (NOT BITWISE & or && OPERATOR)
How about creating all of the UIImageView instances and wrapping them in a single transparent UIView. Then when you want to move all the UIImageViews you'd just need to move their wrapper (said UIView).
update
UIView *wrapper = [[UIView alloc] initWithFrame:bigFrame];
for (size_t i = 0; i<10; i++) {
UIImageView *iv = [[UIImageView alloc] initWithImage:someUIImage];
[wrapper addSubview:iv];
[iv release];
}
[self.view addSubview:wrapper];
[wrapper release];
This code creates an uiview that has 10 uiimageviews inside it. When you move that view (wrapper view) the child image views will move with it.