Can't get a screenshot of UIView to be displayed inside UIImageView - cocoa-touch

Here's my code to get the screenshot of the view:
if (NULL != UIGraphicsBeginImageContextWithOptions){
UIGraphicsBeginImageContextWithOptions(pagedScrollView.frame.size, NO, [[UIScreen mainScreen] scale]);
}else{
UIGraphicsBeginImageContext(pagedScrollView.frame.size);
}
[pagedScrollView.layer renderInContext:UIGraphicsGetCurrentContext()];
screenshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView = [[UIImageView alloc] initWithImage:screenshotImage];
imageView.frame = CGRectMake(imageView.frame.origin.x, imageView.frame.origin.y, imageView.frame.size.width, imageView.frame.size.height);
imageView.backgroundColor = [UIColor greenColor];
[self.view addSubview:imageView];
I'm 100% sure I have the right frame and the right view - the size of UIImageView say about it - but the thing is I can't get my UIImage to be displayed on the UIImageView.
The UIImageView is added on the view and I see a green region on it - but no image inside it, what can be wrong?
I'm using iOS 6 and ARC.

Well, I just tested your code running on my view controller's view and the capture and display worked fine. This leads me to believe that pagedScrollView is probably nil, or has a zero frame.
Either way, you'll be creating an image from a blank context and then passing it to your image view resulting in there not being any visible image.

It turns out I've been taking a screenshot of a long UIScrollView and I was always taking its first page - it had a lot of pages in it and sometimes the layer had nothing in it. It also turns out that UIImage was never released and the memory can effectively store dozens of them.

Related

why is the screenshot taken in objective c blank white image?

I want to take screenshot of the actual screen as viewed by the user on the iphone/ipad then present option for the user to share/save it
I have code and tried several but each time all i get is either a blank white or black image. The image is the same size of the screen.
The screen actual background is plain white and has white buttons on it, i can never capture the buttons.
NSLog(#"Taking The Screenshot");
CALayer *layer = [[UIApplication sharedApplication].delegate.window.rootViewController.layer;
CGFloat scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, scale);
[layer renderInContext:UIGraphicsGetCurrentContext()];
screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//new code for sharing...
UIActivityViewController *shareVC = [[UIActivityViewController alloc] initWithActivityItems:#[screengrab] applicationActivities:nil];
It is because you now have scenes and the app delegate window no longer works.
One way to fix this, if self is a VC, is to change your layer line to
CALayer * layer = self.view.window.rootViewController.view.layer;

Code to make screenshot of an entire UITableView doesn't work anymore in iOS 13

I used the code below for years to be able to capture a screenshot of an UITableView (including hidden rows) and save it to the user's phone gallery or share it.
Since they updated to iOS 13 it doesn't work anymore, it captures only the visible part of the table leaving it blank on the bottom part.
-(UIImage *)imageFromCurrentTable
{
CGRect frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
UIGraphicsBeginImageContext(self.tableView.bounds.size);
[self.tableView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
return [UIImage imageWithData:data];
}
What changed in iOS 13? How this code can be updated? (the code is Obj-C but I will accept also swift answers!)
I have faced this issue. Due to cell Reuse, UIGraphicsGetImageFromCurrentImageContext cannot produce full UITableView structure.
One Way [Not Efficient Way]:
In cellForRowAtIndexPath, we can able to get which UITableViewCell using. Store that cell in [Int: UITableViewCell].
Get screenshot from UITableViewCell.contentView.
Add that screenshot's image as subview to UIView one by one.
Now, UIView having UITableView's contentView as Images.
Get screenshot from UIView.

Taking a snapshot of view that isn't rendered?

Say I have a view that isn't rendered, but I want to convert it to a UIImage. It seems as though I cannot use snapshotViewAfterScreenUpdates:, since it says:
If the current view is not yet rendered, perhaps because it is not yet onscreen, the snapshot view has no visible content.
How can I convert a UIView (e.g. a UILabel) into a UIImage without having to add it to the view hierarchy?
Here is the code I am using:
UILabel *label = [[UILabel alloc] init];
label.text = #"Test";
[label sizeToFit];
UIGraphicsBeginImageContextWithOptions(label.bounds.size, NO, 0.0f);
[label drawViewHierarchyInRect:label.bounds afterScreenUpdates:NO];
UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
This causes the snapshotImage to appear black, as one might expect. Is there an API similar to snapshotViewAfterScreenUpdates: which will allow me to create the UIImage without requiring the view to be rendered?
renderInContext:, as far as I know, does not require the view to be rendered on screen:
[label.layer renderInContext:UIGraphicsGetCurrentContext()];

How to properly position the back button in iOS7

I used this code to use a custom image as the back button in the whole app.
[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:#"back"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:#"back"]];
The image dimensions are 30 x 30.
The code adds the image as the back button but the position is not the correct, as you can see in the following image:
Any ideas on how to properly position the image without modifying its dimensions (at least the visual part of the image (circle + arrow))?
EDIT:
I don't want to use a custom back button because that forces me to disable the swipe/back-gesture in iOS7
EDIT
I think I might have found the trick (in iOS 7 Design Resource -- UIKit User Interface Catalog.)
Under Bar Button Items
Note that a bar button image will be automatically rendered as a template image within a navigation bar, unless you explicitly set its rendering mode to UIImageRenderingModeAlwaysOriginal. For more information, see Template Images.
Under Template Images they have some code to specify the UIImageRenderingMode.
UIImage *myImage = [UIImage imageNamed:#"back"];
UIImage *backButtonImage = [myImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// now use the new backButtomImage
[[UINavigationBar appearance] setBackIndicatorImage:backButtonImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:backButtonImage];
Try creating the UIImage with alignment insets and then set the Back Indicator image.
UIEdgeInsets insets = UIEdgeInsetsMake(10, 0, 0, 0); // or (0, 0, -10.0, 0)
UIImage *alignedImage = [[UIImage imageNamed:#"back"] imageWithAlignmentRectInsets:insets];
[[UINavigationBar appearance] setBackIndicatorImage:alignedImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:alignedImage];
You might also try adjusting the position of the UINavigationBar title text
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics];
Well just follow one of the suggestions to fix the layout and lose the iOS 7 "back gesture", and then fix it with a UIScreenEdgePanGestureRecognizer!
A UIScreenEdgePanGestureRecognizer looks for panning (dragging) gestures that start near an edge of the screen. The system uses screen edge gestures in some cases to initiate view controller transitions. You can use this class to replicate the same gesture behavior for your own actions.
PLEASE SEE EDIT BELOW!!!
I created a custom back button in iOS7 not too long ago. Mine has an arrow and the word back on it. I do think pawan's suggestion is a good start. To create the back button with your custom image you can use,
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:self action:#selector(backButtonClicked)];
[backButton setBackgroundImage:finalImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[backButton setTitlePositionAdjustment:UIOffsetMake(-20, 0) forBarMetrics:UIBarMetricsDefault];
self.navigationItem.leftBarButtonItem = backButton;
My image finalImage is a composite of two different images, but you can just use your "back" image. But I think that is where the problem lies. My image was a composite, you might want to make a composite as well, but put a clear space above your back icon. I placed a clear space to the right of my icon to adjust it's spacing. Here is the code,
UIImage *arrow = [UIImage imageNamed:#"back.png"];
UIImage *wordSpace = [UIImage imageNamed:#"whiteSpace.png"];
CGSize size = CGSizeMake(arrow.size.width + wordSpace.size.width, arrow.size.height);
UIGraphicsBeginImageContext(size);
[arrow drawInRect:CGRectMake(0, 0, arrow.size.width, size.height)];
[wordSpace drawInRect:CGRectMake(arrow.size.width, 0, wordSpace.size.width, wordSpace.size.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
The Image wordSpace is a clear png that I made in photoshop so my new back button image was not stretched. You might want to place a clear png on top, to push the icon down a little. Make the size.height of it in photoshop for what you think the adjustment should be. You might need to futz with this a bit. And make sure to change up the CGSize so that it fits your icon and the clear space.
My word back was a bit off, so I looked at
[backButton setTitlePositionAdjustment:UIOffsetMake(-20, 0) forBarMetrics:UIBarMetricsDefault];
I had to play around with that line a bit to make it look as good as possible but it finally gave me what I wanted with the -20. I even adjusted the second variable which is 0 in mine, this moved the actual icon around. -5 put the icon down way to far, but its another option from the clear png.
Now to deal with the fact that you want it to be an actual back button. Look at the first line of code I posted. The action on the button is #selector(backButtonClicked). So all you need to do is make that method and you should be good to go!
- (void)backButtonClicked
{
NSLog(#"going back");
[self.navigationController popViewControllerAnimated:YES];
}
Hope this helps a bit.
EDIT*****
I was playing around with my code a little bit and found a better way to move the back icon. I just used a ship's wheel because I didn't have the same one that you did, but it will work the same.
Since you don't really want a title you can create the button with this code,
UIImage *image = [UIImage imageNamed:#"781-ships-wheel.png"];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:#selector(backButtonClicked)];
Just change the 781 stuff with your icon's name. Then you can move it around with the following,
[backButton setImageInsets:UIEdgeInsetsMake(20, 0, -20, 0)];
Take a look at this picture.
This shows the icon down considerably, but I wanted to show you the idea. The numbers for the Edge insets are Top, Left, Bottom, and Right. Don't touch the left and right if you don't need to move it that way, change the top and bottom. Notice however, that if you need to move it down by 20 points like I did, (way too much) you need to offset in the negative for the bottom, or the icon will get compressed. This is what it looks like with all zero's.
So you can pretty much move it where ever you want, but you will still have to set up the #selector(backButtonClicked) to make it work like the real back button.
This is Swift 2 version.
The simplest way is like this. Put this code in AppDelegate.'
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let navigationBarAppearace = UINavigationBar.appearance()
let image = UIImage(named: "back-btn")
navigationBarAppearace.backIndicatorImage = image
navigationBarAppearace.backIndicatorTransitionMaskImage = image
return true
}
if your back button has background colour, it may won't work correctly.
Add your icon to asset folder for each resolution like this:
You can try this
self.navigationItem.leftBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 0, 10, 0);
The problem is that your image is too tall. To prove this, first try this code:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,20), NO, 0);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(6,0,4,20));
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorImage = im;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,20), NO, 0);
UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorTransitionMaskImage = im2;
It looks fine. Now change the 20 to 30 in the two CGSizeMake calls:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,30), NO, 0);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(6,0,4,20));
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorImage = im;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,30), NO, 0);
UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorTransitionMaskImage = im2;
The icon is now too high.
So just make your image 20 pixels tall and all will be well.
UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, -2, 0); // or (2,0,0,0)
UIImage *backArrowImage = [[UIImage imageNamed:#"back"] imageWithAlignmentRectInsets:insets];
[[UINavigationBar appearance] setBackIndicatorImage:backArrowImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:backArrowImage];

UIView background image not rotating

I've got an app that has two different background images. The one selected is determined by the orientation. When I start out, I check self.interfaceOrientation, and then go and pick the proper image. However, whenever the view opens, part of the image repeats instead of stretching. I saw a previous answer applying autoresizing masks to an imageview, but there is no imageview that I'm currently using.
Inside the loadView method:
if(self.interfaceOrientation==UIInterfaceOrientationPortrait ||self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
[self.view setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed: #"portrait"]]];
}else{
[self.view setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed: #"landscape"]]];
}
As rishi had pointed out, the issue was caused by the colorWithPatternImage method. What I did to resolve this was to set the background of the view to be a specified image.
UIImageView* bgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed: #"foo.png"]];
[self.view addSubview: bgView];
I also added in flexible width and height so that it would rotate properly.