[iOS]How to make a "snapshot" of the current view's state - iphone

I need to make a snapshot or a screenshot - call it as you like- of the current view and then display it in the modal view.
Because if I just write this in the ModalView's View controller
MyAppViewController *viewController = [[MyAppViewController alloc] init];
self.view = viewController.view;
All the methods of the MyAppViewController are called as well, but I don't need it, I just need to "save" everything that was on the screen when the ModalView appeared and show it in the ModalView's view.
How can I do it?
Thanks in advance!

I would suggest doing this:
Have a method that creates an image out of the contents of the view.
-(UIImage*) makeImage {
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
Create a custom init method for your modal view, and also give your modalView an instance variable that can hold a UIImage something like...
- (id)initWithImage:(UIImage *)temp {
myImage = temp;
}
Then in your modalView, perhaps in the viewDidLoad method, create a UIImageView and set the image to myImage
Hopefully this achieves what you are trying to do.

In iOS 7, you can use one of the new methods in UIView for creating snapshots, for instance:
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates;
which returns a UIView which you can then add as a subview.

UIView *viewSnapShop = [self snapshotViewAfterScreenUpdates:NO];

Related

How to set up a default image

Before i click the image to pull the camera, i want to set up a default image on the imageview that shows a http://www.inc.com/images/avatar/default1.gif to inform that in that imageview goes a person image.
What is the best way to do this procedure?
Best regards.
If you are using a UIImageView the first thing is to create a UIImage object using this code
UIImage *defaultImage = [UIImage imageNamed:#"default.png"];
And then pass it to the UIImageView object if it is a property of your view controller you can use this(imageView is the variable name of UIImageView)
self.imageView.image = defaultImage;
If you are instantiating a new UIImageView in your view controller you can use this
UIImageView *imageView = [[UIImageView alloc] initWithImage:defaultImage];
UIImageView *theImageView;
//assuming that's the declaration in your header
//make sure default1.gif is inside your application bundle
//copied into the resources folder of your xcode project
//this code goes into your -viewDidLoad method
theImageView.image = [UIImage imageNamed:#"default1.gif"];

How to dynamically add a UIImageView to a view and position items?

I have an app where it reads an XML document containing 2 fields (image url and description). If the document has a field that has a valid image URL, I want the view to show the image. Else I just want it to show the description.
The problem I am having is :
how to show the UIImageView dynamically
how to move the UITextView downwards since now I added a UIImageView
Currently I just have a View with a UITextView on it.
Any ideas?
1) -[UIView setHidden:] if the UIImageView is already in place. Otherwise, [[UIImageView alloc] initWithFrame:], then add the image view as a subview to the view.
2) -[UIView setFrame:] (is one option)
The following is what you could do.
Prepare the the view with the UIImageView and the UITextView in the viewDidLoad like this:
-(void) viewDidLoad)
{
[super viewDidLoad];
myImageView = [[UIImageView alloc] init];
myImageView.frame = CGRectMake(x,y,0,0); //you can set it at the right position and even set either the width OR the height depending on where you want the textView in my example I'm gonna assume its beneath the imageView
//other imageView settings
[myView addSubView:myImageView];
myTextView = [[myTextView alloc] init];
myTextView.frame = CGRectMake(x,y,width,heigh+CGRectGetMaxY(myImageView.frame)); //here you prepare the textView for the imageView and the space it takes. But doesn't get hindered by it if it's not there.
//other textView settings.
[myView addSubView:myTextView];
//You would want this loading function about here OR after this loading has been occured.
[self loadImageFromURL];
}
-(void)loadImageFromURL
{
//get your image here.. or not
if(imageHasLoaded) //some condition that gets set when your image has successfully loaded. (This should be done in a delegate (didLoadImageFromURL) if you have such thing prepared.
{
//myImageView.image = theLoadedImage;
myImageView.frame = CGRectMake(x,y,theLoadedImageWidth,theLoadedImageHeight);
//after you set the frame of the imageView you need to refresh the view
[myView setNeedsDisplay];
}
}
And this is how you should dynamically add an imageView with image and frame to some view.

How do I present a UIViewController by zooming?

In my iPad app I have a view controller with a small table view. When you tap on the table view it opens a modal view controller that is a larger and more refined version of the small table view. I would like to create an animation from a pre-rendered image of the large view controller by scaling the image down to be the size of the small table view and zoom it to full screen size and then replace the image with the "real" view controller.
Something like:
LargeViewController* lvc = [[LargeViewController alloc] init];
[self presentModalViewController:lvc byZoomingFromRect:CGRectMake(50,50,200,300)];
I know you can produce an image from a view:
- (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, [[UIScreen mainScreen] scale]);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
But how do I make the view controller draw itself (offscreen) so I can take it's view and scale the image in an animation to fill screen?
Thanks in advance.
I suppose you want to create your very own animation. Last month I played around with something like that. My solution was adding a custom view (maybe taken from a view controller) to the current view as an overlay. This works with layers, too.
First you fetch the Image from your "future" or "present" view controller, like you did in your code example above. Normally the view controllers content should be available while rendering to the context.
Now you have the image. The manipulation of the image must be done by you.
Add the image to a UIImageView. This ImageView can be added as subview or layer. Now you have a layer where you can freely draw above your actual user interface. Sometimes you have to move the layer or view around, so that it perfectly overlays your view. This depends on your view setup. If you are dealing with Tableviews, adding a subview is not that easy. So better use the layer.
After all the work was done, present the new view controller without animation, so that it appears immediately.
Remove the layer or view from your parent view after the work was done, and clean up.
This sounds complicated, but once you've done that you have a template for that. In "WWDC 2011, Session 309 Introducing Interface Builder Storyboarding" apple introduced 'custom segues', where you'll find a mechanism for exactly what you want to do. The code below is a cut out of an older project and is somehow messy and must be cleaned up. But for showing the principle this should work:
-(void) animate {
static LargeViewController* lvc = [[LargeViewController alloc] init];
UIGraphicsBeginImageContextWithOptions(self.bounds.size, view.opaque, [[UIScreen mainScreen] scale]);
[lvc.view.layer renderInContext:UIGraphicsGetCurrentContext()];
// Create a ImageView to display your "zoomed" image
static UIImageView* displayView = [[UIImageView alloc] initWithFrame:self.view.frame];
static UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Add your image to the view
displayView.image = img;
// insert the view above your actual view, adjust coordinates in the
// frame property of displayView if overlay is misaligned
[[self.view] addSubview:displayView];
// alternatively you can use the layer
// [self.view.layer addSublayer:displayView.layer];
// draw the imageView
[displayView setNeedsDisplay];
// do something in background. You may create your own
// construction, i.e. using a timer
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSDate *now = [NSDate date];
NSTimeInterval animationDuration = 3.;
NSTimeInterval t = -[now timeIntervalSinceNow];
while (t < animationDuration) {
t = -[now timeIntervalSinceNow];
// Do some animation here, by manipulation the image
// or the displayView
// <calculate animation>, do something with img
// you have exact timing information in t,
// so you can set the scalefactor derived from t
// You must not use an UIImage view. You can create your own view
// and do sth. in draw rect. Do whatever you want,
// the results will appear
// in the view if you added a subview
// or in a layer if you are using the layer
dispatch_sync(dispatch_get_main_queue(), ^{
// display the result
displayView.image = img;
[displayView setNeedsDisplay];
});
}
});
// now the animation is done, present the real view controller
[self presentModalViewController:lvc animated:NO];
// and clean up here
}
Perhaps you could use something like
CGAffineTransform tr = CGAffineTransformScale(lvc.view.transform, 0.5, 0.5);
to embed a scaled down version of the view in your parent view controller, then present lvc modally and restore scale when the user taps the view.
UIKit takes care of most of this for you. While jbat100's solution could be made to work too, you should be able to do this simply by setting lvc's initial frame to the smaller rect you want to start out at and then when you set the frame too its full size, the implicit animation for changing the frame will handle the zooming animation for you. Each UIView has a CALayer that its content is drawn in and that layer has several implicit animtions setup to animated changes to certain properties such as the frame or position properties. Here is my untested stab at it:
.
.
lvc.view.frame = CGRectMake(50,50,200,300);
[self performSelector:#selector(setFrameToFullScreen) withObject:nil afterDelay:0];
}
- (void)setFrameToFullScreen {
lcv.view.frame = [UIScreen mainScreen].bounds;
}
The performSelector:withObject:afterDelay call will cause setFrameToFullScreen to be called on the next run loop cycle. If you don't do something like that, then only the final frame will be used and the system won't recognize the change in the frame and apply its implicit animation to the views layer.

UIImageView showing the image only after changing tabs in the UITabBarViewController

I have a UIScrollViewController that has a UIScrollView which holds an UIImageView inside.
In loadView of the the scroll view controller I download the image by calling a function, that's supposed to do the downloading on a different thread. I call this function from my loadView and then put the imageview inside the scrollview, and set the scrollview as the view of the controller.
The problem is I can't see the image when I run the program, after clicking a row in the tableView (which is supposed to push the scrollview with the image in it). However, if I change tabss (in the tabbarviewcontroller) and come back to this tab. The image will show.
So I think the image download happens, but I somehow have a problem showing it instantly on the screen. It only appears after I come back to it. What do I seem to be doing wrong? I'm new to threads so I suspect it's a problem with that. Also my code was working before I made it so that it would do the download in another thread, so I'm pretty sure it is related to that.
This is the function in the Photo.m which is an Entity in Core Data. This is supposed to do the download
- (void)processImageDataWithBlock:(void (^)(NSData *imageData))processImage {
NSString *url = self.imageURL;
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_queue_t downloadQueue = dispatch_queue_create("Flickr download", NULL);
dispatch_async(downloadQueue, ^{
NSData *imageData = [FlickrFetcher imageDataForPhotoWithURLString:url];
dispatch_async(callerQueue, ^{
processImage(imageData);
});
});
}
This is my loadView method in the PhotoScrollViewController.m
- (void)loadView {
[image processImageDataWithBlock:^(NSData *imageData) {
UIImage *imageToBeShown = [UIImage imageWithData:imageData];
imageView = [[UIImageView alloc] initWithImage:imageToBeShown];
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
scrollView = [[UIScrollView alloc] initWithFrame:applicationFrame];
scrollView.delegate = self;
scrollView.contentSize = imageToBeShown.size;
[scrollView addSubview:imageView];
self.title = image.title;
self.view = scrollView;
}];
}
Edited to add extra information that was added as an answer
Adding [super loadView] to my loadView method solved the issue. However, the documentation says that I shouldn't be calling super loadView from my loadView.
I tried moving the code to viewDidLoad and that works as well. But really this is the code that's setting the view so I feel like I should be putting it in loadView. But then it doesn't work when I use this multi threading mechanism for download.
Is this because the download is somehow interfering with me setting the view in loadView?
It seems loadView completes before the image download is done. You need some way of calling [imageView setNeedsDisplay] when image download is complete. You need some method that gets called when image download is done that accesses the imageView (perhaps using viewWithTag) from the scrollView and calls [imageView setNeedsDisplay];

UIImageView subclass does not display

I am using a custom subclass of UIImageView, but I can't figure out why it's not being displayed.
The relevant code from my UIImageView subclass:
-(id) initWithImage:(UIImage*)image{
if(self = [super initWithImage:image]){
}
return self;
}
And from the view controller for the view that will be displaying my subclass:
UIImage *zoomRatateSliderHorizontal =
[[UIImage imageNamed:#"ZoomRotate Slider Horizontal.png"]
stretchableImageWithLeftCapWidth:(75.0)/2-1.0 topCapHeight:0];
scaleHorizontalControl = [[ViewTransformationController alloc]
initWithImage:zoomRatateSliderHorizontal];
scaleHorizontalControl.onScreenFrame = CGRectMake(0.0, 5.0,
self.view.frame.size.width,
scaleHorizontalControl.image.size.height);
scaleHorizontalControl.frame = scaleHorizontalControl.onScreenFrame;
scaleHorizontalControl.offScreenFrame = CGRectZero;
[self.view addSubview:scaleHorizontalControl];
Before I was subclassing, I had no trouble with the following in the view controller:
UIImage *zoomRatateSliderVertical =
[[UIImage imageNamed:#"ZoomRotate Slider Vertical.png"]
stretchableImageWithLeftCapWidth:0 topCapHeight:(75.0)/2-1.0];
scaleOrRatateViewVertical = [[UIImageView alloc]
initWithImage:zoomRatateSliderVertical];
scaleOrRatateViewVertical.frame = CGRectMake(-zoomRatateSliderVertical.size.width,
zoomRatateSliderHorizontal.size.height+5.0+5.0,
zoomRatateSliderVertical.size.width,
465.0 - zoomRatateSliderHorizontal.size.height-10.0-5.0);
[self.view addSubview:scaleOrRatateViewVertical];
Using break points I checked the frame and image being passe to my class, and they both appear to be valid and desired.
Any advice on what I'm doing wrong I'd greatly appreciate.
The Apple Docs read:
The UIImageView class is optimized to draw its images to the display. UIImageView will not call drawRect: a subclass. If your subclass needs custom drawing code, it is recommended you use UIView as the base class.
So I'd subclass UIView and work from there.