Where is the first good moment to take a screenshot in UIViewController - ios

I would like to make a screenshot of my view when view is loaded. I use this method:
- (UIImage *)screenshot {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0.0f);
if( [self respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)] ) {
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
} else {
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
}
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenshot;
}
The screenshot method was tested and works well. I am using the method in my UIViewController in viewDidLoad. The problem is that the result is a grey image! It's mean that view is not loaded in viewDidLoad method. The same issue can be observe in viewDidAppear. So viewDidLoad and viewDidAppear are too early. I have made workaround in viewDidLoad method:
__weak MyUIViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^() {
UIImage* image = [weakSelf.view screenshot]
// do sth with image...
});
How to make a screenshot without dispatach_async ?

Try This:
- (UIImage *)screenshot {
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, [UIScreen mainScreen].scale);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
UIView-JTViewToImage project also usefull.

You could also try the snapshotViewAfterScreenUpdates method of UIView:
[self.view snapshotViewAfterScreenUpdates:YES];

Try this
New API has been added since iOS 7, that should provide efficient way of getting snapshot
snapshotViewAfterScreenUpdates: renders the view into a UIView with unmodifiable content
resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets : same thing, but with resizable insets
drawViewHierarchyInRect:afterScreenUpdates:
same thing if you need all subviews to be drawn too (like labels, buttons...)
Hope it helps.

Related

How to prevent the flash when calling drawViewHierarchyInRect:afterScreenUpdates: on iOS 8?

Under iOS 8 a call to UIView's
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
results in a 1 frame flash of the image. You can see it for a split second on the screen. It only happens when afterScreenUpdates parameter is YES.
This is my complete code to take screenshot:
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, sf);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextConcatCTM(ctx, [self.layer affineTransform]);
if ([self respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { // iOS 7+
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
} else { // iOS 6
[self.layer renderInContext:ctx];
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(ctx);
UIGraphicsEndImageContext();
Is there a workaround?
Provide #3x launch images to be used in iPhone6 and iPhone6 plus and this problem goes away.
Here's a possible workaround that solved my issue.
Use CATransaction.completionBlock to call UIView drawViewHierarchyInRect:afterScreenUpdates with the screen updates flag set to NO. I used this to workaround the same problem. It is semantically equivalent to setting the flag to YES in my case since I'm only invoking it after the current CATransaction has finished and the screen is in the desired state. That said, in my case, I don't have any animations, so this is pretty much immediate and captures exactly what I want. If there were animations, this may not be appropriate.
For my case, I have a collection view that is a collection of files. Each item is rendered as a snapshot of what the file looks like once the user opens it. Some files cause the UI to have hundreds of layers. When these complicated views were being snapshotted, I would get a black screen flash; or the last/current animation to partially rerun. My collection view is w/in a navigation controller, I was seeing partial reruns of the pop animation. I also have noticed reruns of rotation animations. For simpler files, there was often no issue. I noticed this on my iPad Air (iOs 8.1) and on various simulators. I tried putting appIcons and launchImages in place but they had no effect.
Here was the original code. It initializes a view controller, then adds the controller's view to the a utility view.
UIViewController *vlController = [[ComplicatedViewController alloc]init];
UIView *innerView = vlController.view;
[v addSubview:innerView];
innerView.transform = CGAffineTransformMakeScale(scalar, scalar);
innerView.center = CGRectCenterPoint(v.bounds);
auto sz = v.bounds.size;
innerView.bounds = {{0,0}, {sz.width/scalar, sz.height/scalar}};
UIGraphicsBeginImageContextWithOptions(v.bounds.size, YES, 0);
[v drawViewHierarchyInRect:v.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[innerView removeFromSuperview];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[v addSubview:imageView];
The new code just moves the bottom half to the completion block of UIView animateWithDuration:completion. Notice that the duration of the UIView animation is 0, but that still seems to give that extra refresh cycle to update the screen with the new UI.
UIViewController *vlController = [[ComplicatedViewController alloc]init];
UIView *innerView = vlController.view;
[UIView animateWithDuration:0.0 animations:^{
[v addSubview:innerView];
innerView.transform = CGAffineTransformMakeScale(scalar, scalar);
innerView.center = CGRectCenterPoint(v.bounds);
auto sz = v.bounds.size;
innerView.bounds = {{0,0}, {sz.width/scalar, sz.height/scalar}};
} completion:^(BOOL finished) {
UIGraphicsBeginImageContextWithOptions(v.bounds.size, YES, 0);
BOOL drawResult = [v drawViewHierarchyInRect:v.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[innerView removeFromSuperview];
[self add:image forFile:v.filename withOrientation:v.orientation];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[v addSubview:imageView];
}];

How to take snapshot of a viewcontroller without loading in screen in ios?

In my iPAD application, I have two viewcontrollers, first viewcontroller has a button, I want to get the snapshot of second viewcontroler when I click on that button, without loading the second viewcontroller in iPAD screen.
If I load the viewcontroler and then take snapshot then it is working but my requirement is to do the same without loading the viewcontroler in screen.
Please give idea or link or code snippet.
try this:-
Make instance of VC that you want to take screen shot, and then pass the object in this method.
+ (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame {
// Create a new context the size of the frame
UIGraphicsBeginImageContextWithOptions(frame.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Render the view
[view.layer renderInContext:context];
//[view drawRect:frame];
// Get the image from the context
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup the context you created
UIGraphicsEndImageContext();
return renderedImage;
}
This solution is based on Dheeraj Kumar's answer, but this will work if your view contains SpriteKit contents as well. It requires iOS7, though.
The code in Swift, for the ViewController with the SpriteKit view,
private func takeScreenshot() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.mainScreen().scale)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Not part of the question, but you can then share your screenshot very easily,
if let image = screenshot as? AnyObject {
let activity = UIActivityViewController(activityItems: [image], applicationActivities: nil)
self.presentViewController(activity, animated: true, completion: nil)
}
Just instantiate your second view controller, and take a screenshot, like:
- (void)foo
{
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
UIImage *screenShot = [self imageFromView:vc.view];
// Do something with screenShot
}
- (UIImage *)imageFromView:(UIView *) view
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, [[UIScreen mainScreen] scale]);
} else {
UIGraphicsBeginImageContext(view.frame.size);
}
[view.layer renderInContext: UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

How can I programmatically put together some UIImages to have one big UIImage?

This is my code:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
// at this point the webView scrolled to the next section
// I save the offset to make the code a little easier to read
CGFloat offset = _webPage.scrollView.contentOffset.y;
UIGraphicsBeginImageContextWithOptions(_webPage.bounds.size, NO, 0);
[_webPage.layer renderInContext:UIGraphicsGetCurrentContext()];
viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
// if we are not done yet, scroll to next section
if (offset < _webPage.scrollView.contentSize.height)
{
[_webPage.scrollView scrollRectToVisible:CGRectMake(0, _webPage.frame.size.height+offset, _webPage.frame.size.width, _webPage.frame.size.height) animated:YES];
}
}
In which I save an undefined number of screenshots (UIImages) by scrolling the web view. This works, I have in my photo gallery all the parts of the web page.
But I don't want parts, I want ONE long UIImage. So how do I put (one by one?) my UIImages together?
You can write a UIImage category to do that
UIImage+Combine.h
#import <UIKit/UIKit.h>
#interface UIImage (Combine)
+ (UIImage*)imageByCombiningImage:(UIImage*)firstImage withImage:(UIImage*)secondImage;
#end
UIImage+Combine.m
#import "UIImage+Combine.h"
#implementation UIImage (Combine)
+ (UIImage*)imageByCombiningImage:(UIImage*)firstImage withImage:(UIImage*)secondImage {
UIImage *image = nil;
CGSize newImageSize = CGSizeMake(MAX(firstImage.size.width, secondImage.size.width), firstImage.size.height + secondImage.size.height);
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(newImageSize, NO, [[UIScreen mainScreen] scale]);
} else {
UIGraphicsBeginImageContext(newImageSize);
}
[firstImage drawAtPoint:CGPointMake(roundf((newImageSize.width-firstImage.size.width)/2), 0)];
[secondImage drawAtPoint:CGPointMake(roundf(((newImageSize.width-secondImage.size.width)/2) ),
roundf((newImageSize.height-secondImage.size.height)))];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
and then you can call the function in your code with:
UIImage *img = [UIImage imageByCombiningImage:image1 withImage:image2];
This will draw a new image that has the width of the biggest of the two images and the height of both images combined. image1 will be at the top position and image2 below that.

pass image with textField to another viewController

I have two view controller class on first i have a image view plus textField inside the imageView and on second view controller there is a imageView. First view controller have a done-button, on clicking done-button i want imageView with textField pass to the secondViewController imageView.
Is there any way to do it?
Please suggest me.
- (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame
{
// Create a new context the size of the frame
CGSize targetImageSize = CGSizeMake(frame.size.width,frame.size.height);
// Check for retina image rendering option
if (NULL != UIGraphicsBeginImageContextWithOptions)
UIGraphicsBeginImageContextWithOptions(targetImageSize, NO, 0);
else
UIGraphicsBeginImageContext(targetImageSize);
CGContextRef context2 = UIGraphicsGetCurrentContext();
// The view to be rendered
[[view layer] renderInContext:context2];
// Get the rendered image
UIImage *original_image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return original_image;
}
Try this function for Render image
And the below one for merge them
- (UIImage *)mergeImage:(UIImage *)img
{
CGSize offScreenSize = CGSizeMake(206, 432);
if (NULL != UIGraphicsBeginImageContextWithOptions) UIGraphicsBeginImageContextWithOptions(offScreenSize, NO, 0);
else UIGraphicsBeginImageContext(offScreenSize);
CGRect rect = CGRectMake(0, 0, 206, 432);
[imgView.image drawInRect:rect];
[img drawInRect:rect];
UIImage* imagez = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imagez;
}
You can change coordinates and height,width according to your requirement.
Example:
This method is declared in ClassB.h
- (void) setProperties:(UIImage *)myImage MyLabel:(UILabel *)myLabel;
This above is implemented in ClassB.m
- (void) setProperties:(UIImage *)myImage MyLabel:(UILabel *)myLabel
{
//assign myImage and myLabel here
}
Then in ClassA
ClassB *classB = [[ClassB alloc] init];
[classB setProperties:myImage MyLabel:myLabel];

Resizing image to fit UIImageView

I am very new to objective c and I'm just getting my bearings. I want to do something really simple but it proves to be quite a challenge:
I am trying to display an image into an UIImageView. The image I'm showing is large and I want it scaled down to fit the UIImageView. I tried setting the AspectFit View mode but the image gets displayed to the original size and is clipped by the UIImageView. My code is below:
- (void)changeImages
{
UIImage* img11 = nil;
img11 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:#"dog" ofType:#"jpeg"]];
u11.contentMode = UIViewContentModeScaleAspectFit;
u11.image = img11;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self changeImages];
}
Can anyone shed some light on this please?
Thanks!
Hi I would try this...
- (void)changeImages
{
UIImage *img11 = [UIImage imageNamed#"dog.jpeg"];
u11.contentMode = UIViewContentModeScaleAspectFit;
u11.clipsToBounds = YES;
[u11 setImage:img11];
}
- (void)viewWillAppear:animated
{
[super viewWillAppear:animated];
[self changeImages];
}
This will scale the image (up or down) so that it fits inside the imageView. Having clipsToBounds isn't necessary but will stop the image from displaying outside the frame of your imageView.
HTH.
Add to your UIViewController.m:
-(UIImage *)resizeImage:(UIImage *)image imageSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0,0,size.width,size.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// here is the scaled image which has been changed to the size specified
UIGraphicsEndImageContext();
return newImage;
}
Using:
UIImage *image = [UIImage imageNamed:#"image.png"];
CGSize size = CGSizeMake(50, 63); // set the width and height
UIImage *resizedImage = [self resizeImage:image imageSize:size];
I hope it helps.
CGSize size=CGSizeMake(79, 84);//set the width and height
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0,0,size.width,size.height)];
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
//here is the scaled image which has been changed to the size specified
UIGraphicsEndImageContext();
This works for sure and don't forget to import QuartzCore FrameWork..
Have a Happy Coding (^_^)....
You can also set View:Mode to Aspect Fit in the Attributes Inspector in Interface Builder
I did the following and it helped
change the mode to "aspect fill" from the default value "Scale to fill"
and add a line of code as follows (I did it in a cell configuration):
cell.photo.clipsToBounds = true
I know this is old but I wanted to add a response based on the Stanford 193p 2017 lecture 11 (around 18:45) and for anyone looking in swift as this is the first search result that showed up for me.
Basically, subclass UIView and make it look like:
class U11: UIView {
var myImage: UIImage? { didSet { setNeedsDisplay() }}
override func draw(_ rect: CGRect) {
myImage?.draw(in: bounds)
}
}
Then set the image with:
func changeImage() {
if let img11 = UIImage(named: "dog.jpeg"){
u11.myImage = img11
}
}
This is super simple and the image takes up the whole space inside of the views bounds.

Resources