How to resize (shrink) a UIView without pixellation? - iphone

In my app, I'm creating a full-screen view and then shrinking it down (to about 1/10th of its original size) so that I can later animate it to full-size. The problem is that the view looks terrible at that size, and is highly pixellated.
Here's the full-size view:
And here's the shrunken view in-place:
I'm shrinking the view using setFrame: - is there some way to tell iOS to use high-quality interpolation? Or am I already getting the highest-quality interpolation and this is the best it can do?

I'd recommend turning the view into an image before you shrink it (I assume currently you have your day view, made up of lots of subviews, which you then just shrink by adjusting the frame).
If you look you'll see this is how Apple achieve it: rather than literally shrinking the view, they will capture the contents of the view as an image, replace the view with that image, and then shrink the image. You'd do this for a number of reasons: efficiency (you can get rid of the shrunken views and replace them with a single UIImageView), speed, and also quality.
To save your UIView as an image you should do something like:
UIGraphicsBeginImageContext(myView.bounds.size);
[myView.layer renderInContext:UIGraphicsGetCurrentContext()];
viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
...then replace your view with the image, and shrink it.

Related

Exporting a hi-res image from a UIView that contains other views

I am trying to export an image from a UIView that contains a UIImage view and some labels. I am not sure I am going about this the right way. I want to export everything in the view and maintain the layout. I want to export at 1536 x 2048.
I am using the following code with renderInContext to grab an image of the main view (UIView). Kind of works, but the layout gets messed up, basically the layout changes and the labels do not scale properly. Is renderInContext the right way to go for something like this? Is there a better way?
you can download the whole project here: http://tinyurl.com/7qvhqtp
UIGraphicsBeginImageContext(CGSizeMake(1536, 2048));
viewOutput.frame = CGRectMake(0, 0, 1536, 2048);
[[viewOutput layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(image,nil,nil,nil);
This is the code I use to save the current UIView as an image. The layout gets saved perfectly.
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I'm not 100% sure, but there might be some issues with UIGraphicsBeginImageContext vs UIGraphicsBeginImageContextWithOptions.
On the other hand the problem might be in your view's subview's autoresizing mask if your view has the autoresizesSubviews set to YES. I'd try to disable it if you do not have the support for rotations or at least before changing your view's frame.
The only thing you did to get a bigger image was to change the frame but the labels did not have the correct autoresize masks nor did their font size change. You can clearly see this in the "after running code" image that the label did neither move nor change size.
First, If you wan the label to change its frame when the superview's frame changes (are the labels subviews of the image? If not you may need to calculate their new frames yourself) then you should give it a suitable auroresizing mask. In your case you would want it to have a fixed distance to the bottom of the screen and a flexible distance to the top. You would also want the distances to the left and right to be fixed so that the width can be flexible.
This only leaves you with the font size problem. You should calculate the scale factor (how many times bigger everything gets) and multiply the current font size with that scale factor.

iPhone Horizontal Scrolling

I am trying to create an app with horizontal scrolling, so that one would be able to scroll horizontally through a series of images. I watched the WWDC Session 104 video on this, and while they made an interesting app, they flew through the basics of it very quickly.
I understand using the UIScrollView, and that I have to enable paging. After that they say that I should add more views as subviews of the scrollview, but I am not clear on how to do that. I am also not clear on how I add my images to those views.
As you can probably tell I am pretty new at this so any help would be appreciated.
You want to look into UIImageView. It's a view specifically for holding images.
When you add your images, you want to set their rects (probably using initWithFrame: for each UIImageView) so that:
the first image is at 0,0
the second image is at 320,0
third is at 640,0 (etc)
I.e. each image is 320 pixels right of the previous.
The final step is to set the contentSize for your UIScrollView -- this is a CGSize which describes the total size of the scroll view.
If you have 3 images, you would then set it to (320*3) * 480 using e.g.
myScrollView.contentSize = CGSizeMake(320*3, 480);
A lot of people, when they initialize the scroll view, have a for loop or similar which steps through the images they want to display. These for loops tend to look something like this:
CGFloat scrollWidth = 0.f;
for (UIImage *someImage in someNSArrayWithImages) {
UIImageView *theView = [[UIImageView alloc] initWithFrame:
CGRectMake(scrollWidth, 0, 320.f, 480.f)];
theView.image = someImage;
[myScrollView addSubview:theView];
[theView release];
scrollWidth += 320.f;
}
myScrollView.contentSize = CGSizeMake(scrollWidth, 480.f);
This way you'll get things lined up and you'll get the content size for you at the same time.
If you want to make it so that the scroll view "intelligently" scrolls to each image and stops when people slide left/right, you can do myScrollView.pagingEnabled = YES.
Hope that helps get you going.
Assuming you have "infinite" images, putting them all there at or before launch time in a huge UIScrollView will not be an option. (there is a limit to the size of a UIView)
The way I solved it: Make a UIScrollView covering the whole screen. It's content should be a UIView of 3*320 width and 480 height, extending 320px left and 320px right.
Put 3 UIImageView's in it, left, middle and right. Set paging=YES, so the uiscrollview clips to the 3 "pages" you've created.
Make sure your class is the delegate of the uiscrollview, and listen for
-(void)scrollViewDidEndDragging:(UIScrollView*)sv willDecelerate:(BOOL)notFinished
-(void)scrollViewDidEndDecelerating:(UIScrollView*)sv
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView*)sv
and make the appropriate transitions on hitting paging boundaries; shift images and set ContentOffset so you're looking at the center image again.
I suggest you make this first, and only then read on...
Then you will hit on a bug, documented here UIScrollView - (bounces = NO) seems to override (pagingEnabled = YES) and here http://www.iphonedevsdk.com/forum/iphone-sdk-development/935-paging-uiscrollview.html, which makes that you cannot disable bouncing and have paging enabled at the same time. So enable bouncing, and subclass UIScrollView, overruling setContentOffset in there to prevent bouncing. (having bouncing really enabled will make for a rather unusual user experience)
Have a look at Apple's PageControl sample code. It's fairly short and easy to follow so you'll get the gist of setting up a project where multiple view controllers are loaded as you swipe horizontally.
Once you have this setup then it's the view controller's responsibility to load its own content (in your case, an image). You should make sure you understand how to load images first (using threads, etc) before you tackle paging, etc.
Think of it as two independent tasks. The view control is responsible for loading and displaying an image. The scroll view with paging just tells the appropriate view controller when to load itself (it doesn't care what the view controller does once its loaded)
Good luck!

how to draw an UIImageView into -drawRect of another view?

I have several UIImageView objects which have been rotated and scaled. Because they are just background images, and represent an non-highlighted-state, I want to draw them to the canvas of the view so that I can get rid of them in memory. The view has a big bitmap anyways, so it would save a lot of memory to put them in there rather than adding as subview.
It seems I can only call something like -drawInRect for an UIImage, but how about an UIImageView with transforms on it? Oh yes, and it's positioned with frame origin.
I just want to draw it to another UIView's bitmap the same way as it appears when adding as subview.
Using drawInRect: with a rect of different origin and size of the image, you can effectively make any scaling and translation you want. You can also handle rotations with the [CGContextRotateCTM][1] function (CTM = current transformation matrix). There are other CGContext..CTM functions as well, so you have multiple approaches.

iPhone - increase UIImageView size and keep the image size fixed

I have a custom UIImageView class which I use to handle multi-touch events on the UIImageView. When the user touch begins, I want to increase the UIImageView's frame but keep the UIImage size fixed.
I tried changing the UIImageView's frame and then calling the drawInRect: method of UIImage to keep the UIImage's size fixed. But this is not working.
The contentMode for the UIImageView is set as ScaleAspectFit and as soon as I increase the frame size of the UIImageView, the UIImage size also increases (and is not affected by the drawInRect:)
Can someone please tell me how I can achieve this?
Thanks.
Adding more details
What I am trying to do is this
Place a UIImageView on the screen with the size same as the size of the image
When the user selects the image, anywhere he touches, the image edits as if the user is doing multi-touch with the image
If I increase the size of the imageview to detect touches any where, the image size also increases... Hope that makes things clearer!
Thanks
There may well be other ways to do it, but I think the UIImageView is doing what it's intended to do here.
What do you want the area of the view not covered by the image to look like? Be transparent? Have a solid colour?
Why do you want to do this? Is it to capture touch events from a wider area than that under the image itself?
If you have a good reason for needing to do this I would create a new view, probably just a plain UIView (with background set to transparent colour), and add the UIImageView to that. Make the plain view the one you resize.
I haven't specifically tried this, but I think it would work:
imageView.contentMode = UIViewContentModeCenter;

UIImage and UIImageView problem

set a UIImageView.image property to a UIImage is unable add the image to the view,however if i set the imageview frame property,it works.can anyone tell me how it's happen?
and what will happen when i apply the UIImage instance method[drawInRect:] redraw the image frame which is larger than the view frame?i have tried but nothing happen,and what is this function doing actual?
Generally, how it works is, you add an Image to a UIImageView and then add the UIImageView to a UIView to display it on the screen. You can do this programmatically, or using Interface Builder. Optionally, you can create a CGRect (based on a frame, if you like), use this as the bounds for the UIImageView (or you can frame the UIView). There are several ways to do what you want to do.
It's hard to tell without seeing your code. It could be a number of things. You could be inserting the view with the image behind the currentView? You may not be retaining the UIImageView. We can't read minds here necessarily, (sometimes we do). But you'll need to be more helpful if you want a solid answer.
Or just take a look at this:
UIImageView Docs