How to refresh view in objective-c - 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.

Related

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 */
}

UIView creation and positioning

I have in my controller two UIView members, progressLineView and buttonsView. At some point I call this method:
- (void) drawPlayProgressLine{
progressLineView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1, buttonsView.frame.size.height)];
progressLineView.backgroundColor = [UIColor whiteColor];
[buttonsView addSubview:progressLineView];
}
Everything works fine, and I also have a method that changes the position of the view:
- (void) moveProgressLine{
CGRect frame = progressLineView.frame;
frame.origin.x++;
progressLineView.frame = frame;
}
After the moveProgressLine method is called a few times and I want to call drawPlayProgressLine again, instead of completely moving the view to the starting position, it creates a new view. The more drawPlayProgressLine is called, the more views I get on my screen but I only need one.
I don't understand how this can happen when I'm creating only one object. How can I move the view instead of having a new one created each time? And another question: how can completely remove it (until the drawPlayProgressLine method is called to create it again)
I don't understand how this can happen when I'm creating only one object.
You create a new view every time you call your -drawPlayProgressLine method. Call it 10 times, you get 10 views.
How can I move the view instead of having a new one created each time?
Don't create the view each time through -drawPlayProgressLine. Instead, you can do either of:
Create progressLineView once, when the view controller's view hierarchy is created. -viewDidLoad is a perfect place for that sort of thing.
Check the value of progressLineView and create it only if it is currently nil.
Whichever you choose, assuming progressLineView is an instance variable, you can do exactly what you're doing in your -moveProgressLine method. That is, just use progressLineView as though it already exists, because it does. BTW, an easy way to move a view is to modify it's center property:
CGPoint *c = progressLineView.center;
c.x += 25.0;
progressLineView.center = c;
And another question: how can completely remove it (until the
drawPlayProgressLine method is called to create it again)
One approach is to simply hide the view when you're not using it. Another is to remove it from its super view (and release it if you've retained it), and then set your progressLineView to nil. So, if progressLineView is an ivar, do this:
[progressLineView removeFromSuperview];
[progressLineView release]; // if you're not using ARC and have retained it
progressLineView = nil;
you should just check if its created yet before creating it and move it if necessary:
- (void) drawPlayProgressLine{
if(progressLineView == nil)
{
progressLineView = [[UIView alloc] init];
progressLineView.backgroundColor = [UIColor whiteColor];
[buttonsView addSubview:progressLineView];
}
progressLineView.frame = CGRectMake(0.0, 0.0, 1, buttonsView.frame.size.height);
}
You are probably not invalidating the parent view. New objects are not created, but rather their presentation is left on the screen after you move them.
As for the second question:
[progressLineView removeFromSuperview];
[progressLineView release];
progressLineView = nil;

Advice for horizontal scroll view IOS

I'm trying to create horizontal scroll view(s). Each view containing one image. [done]
Initially when app is opened I'd display couple of images i.e 3, so user can scroll back and forth between images. [done]
However I want to be able to go to another view controller and pick another image(s) maybe two for example and display five images in the scroll view instead of displaying 3 initially.
How would one do that? to "re-refresh" the initial scroll view ?
Update
Should I use delegate for this communication between view controllers? or how is this done?
1 main controller, other containing image selection?
This part above and much more explained by article here.(not advertising, I hope it will help someone as well).
Bounty update part 1 :
I think now that I found my way around delegates, I have additional question that I cannot find answer to, all examples I saw were about updating table views.
Bounty part 2 :
If I were to have a scrollview inside my view controller and some nsimages inside scroll view. i.e :
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *images = ...some images array;
for (int i = 0; i < images.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *subview = [[UIImageView alloc] initWithFrame:frame];
subview.image = [images objectAtIndex:i];
[self.scrollView addSubview:subview];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * images.count, self.scrollView.frame.size.height);
}
And let say my view controller implemented some delegate method didAddImage or didRemoveImage.
Meaning that about images array would get updated.
Actual bounty question :
How would one actually "tell" the view controller, O.K now your scrollview has one more image to display, please re-fresh or reload?
If I were to have a table view instead scroll view and was inserting images I'd do it something like this(in my delegate method) :
-(void) mockDelegatetablemethod:(...) ...{
[self.images addObject:image];
NSIndexPath *indexPath =
[NSIndexPath indexPathForRow:[self.images count] - 1
inSection:0];
[self.tableView insertRowsAtIndexPaths:
[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self dismissViewControllerAnimated:YES completion:nil];
}
How would one do this to the scrollview?
Bounty update part 3:
This is "simple" case described above, naturally I'd have to support removing images also as well as if one wants to remove all images and add some new.
How would one actually "tell" the view controller, O.K now your
scrollview has one more image to display, please re-fresh or reload?
Well, you would just add the new image as a subview of the scroll view. So something like:
- (void)addImage:(UIImage*)image {
[images addObject:image];
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * images.count;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *subview = [[UIImageView alloc] initWithFrame:frame];
subview.image = image;
[self.scrollView addSubview:subview];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * images.count, self.scrollView.frame.size.height);
}
Or you could have a method that goes and "resets" the scroll view by removing all subviews and then re-adds all the images in your images array. Then call this method in viewDidLoad instead of your code there so that you're not duplicating code.
Basically, there's no silver bullet here. You roll your own.
You're going to need make your image scroller a class that you can tell to load up certain images. You'll need a reference to the existing image scroller, pass that pointer into your other "pick more images" controller. Then the "pick more images" controller can tell the image scroller that there is a new list of images.
This is already implemented by Apple in their iOS sample project "PhotoScroller". It has view reuse as well. They even have a WWDC 2010 video on this. I think its advanced scrollview techniques OR designing apps with scrollviews.
Shouldn't be hard to port over to mac

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.

When should I deallocate memory, if all?

I’ve read all the iOS memory allocation/deallocation basics, but can’t find anything on the following:
I’ve created a small app, that pretty much lists a grid of UIButtons and clicking on any of those, it adds a UIScrollView to the current controller view (and adds a bunch of UIView and UIWebView’s inside etc).
On adding the UIScrollVIew, I also add a UIButton, that takes me back to the "home grid" and then removes the UIScrollView from the superview.
I release all the things I’m retaining/allocating etc and when I check the app with Instruments, it doesn’t show any memory leak.
But every time I tap on any of the UIButton objects, I allocate more memory (according to Instruments) and it keeps growing – and "reopening" the same kind of UIScrollView from a button always adds more memory allocations.
If I simulate a memory warning in the simulator, it deallocated a bit of the memory and I can then keep growing it again.
So here’s my question: should I bother with trying to deallocate this somehow manually? And if so, where should I actually do this? I’m quite new to Obj-C, so I think I have most of the basics covered, but more advanced topics still require some help.
Creating the grid:
UIScrollView *grid = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
[[self view] addSubview:grid];
[grid release];
Adding buttons (in a for loop):
UIButton *slotItem = [[UIButton alloc] initWithFrame:CGRectMake(((float) slotWidth * j), ((float) slotHeight * i), (float) slotWidth, (float)slotHeight)];
[grid addSubview:slotItem];
UIWebView *buttonWebThumb = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, slotWidth, slotHeight)];
[buttonWebThumb setBackgroundColor:[UIColor clearColor]];
[buttonWebThumb setOpaque:NO];
[buttonWebThumb loadHTMLString:[NSString stringWithFormat:#"%# %# %#", htmlTop, [[row objectAtIndex:j] objectAtIndex:1], htmlBottom] baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
buttonWebThumb.scalesPageToFit = YES;
[slotItem addSubview:buttonWebThumb];
buttonWebThumb.userInteractionEnabled = NO;
buttonWebThumb.exclusiveTouch = NO;
[buttonWebThumb release];
[slotItem addTarget:self action:#selector(showPages:) forControlEvents:UIControlEventTouchUpInside];
[slotItem release];
showPages method then creates another UIScrollView and adds 1-10 separate UIWebView’s in there and adds a “close” button to the new UIScrollView.
You're probably seeing this because you aren't removing all elements from the view before leaving it.
For example, if you exit the view with a method like this, do this:
- (id) viewExitAction: (id) sender {
id elem;
for(elem in buttons) {
[elem removeSelfFromView];
}
// etc
}
This will automatically decrease the retain counts of these objects down to zero. (Provided you added them to the view and then released them, as you're supposed to.)
Otherwise, the viewDidUnload and dealloc methods never get called.
Dealloc is invoked indirectly through the release method, and you should never call it directly.NSObject Manual on dealloc