Merge a UIImageView with a UIView that contains different subviews - ios

I have a UIImage (called image) that i want to merge with another UIView (called overlayView), that contains different subviews eg. UILabels, UIImageViews and so on, to make a new UIImage (called mergedImage). Here's my code until now:
-(UIImage *) mergeViewWithImage: (UIImage *) image{
//The mergedImage need to be the same size as the image
UIGraphicsBeginImageContext(image.size);
//I'm doing this to get the layer, when i renderInContext, other ways?
UIImageView *temp = [[UIImageView alloc] initWithImage:image];
//This is working, the view gets its new frame, but the subviews won't scale up
[overlayView setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
[temp.layer renderInContext:UIGraphicsGetCurrentContext()];
[overlayView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *mergedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//Need to set the frame to the initial one
[overlayView setFrame:CGRectMake(0, 0, 320, 320)];
return mergedImage;
}
This screenshot is what I really want it to look like
what you see is a AVCaptureVideoPreviewLayer (320*320), in the square, and I have a UIView, called overlayView (320*320), on top. When i press the "Take"-button, I do certain scaling, cropping and so on with the AVCaptureStillImageOutput, then i run the mergeViewWithImage-method. At this point the image is 968*968, but the overlayView is still 320*320 (has a blue boarder, just to know where it is). When I setFrame: to the overlayView, the the subviews, eg. the example-image of Luca Toni in the left lower corner doesn't scale, as you see in this screenshot
This is what i get
So you see, the background image (a white wall) stays the same, and the overlayView did resize, but the image of Luca Toni is much smaller, it wont scale... Please, help.
I've tried a lot of things, like setting this:
//imageOne is the picture with Luca Toni...
[[overlayView imageOne] setAutoresizingMask: UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
The overlayView is made in the Interface Builder as a .xib, which is a UIView with a UIImageVIew (imageOne), and has its own UIVIew custom class (nothing here yet, only the reference to imageOne (Luca Toni)). The overlayView is initiated in the MainViewController, like this:
[self overlayView:[[OverlayView alloc] init]];
overlayView = [[[NSBundle mainBundle] loadNibNamed:#"OverlayView" owner:self options:nil] objectAtIndex:0];
and added to the main view like this:
[[self view] addSubview:overlayView];

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];
}];

iOS: ImageView filled with a picture

I have a table that contains the path to the picture, saved on user's library. Then, I pass this path to a View that contains just a imageView. I want to fills this ImageView with the picture. Like WhatsApp (when you click on a profile's picture). But, my picture it's always cropped or distorted. I tryed different ways to do this, but I didn't find the best way:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.translucent = NO;
UIImage *picture = [UIImage imageWithContentsOfFile: self.picturePath]; //I passed this path from the previous View
UIImageView* imageView = [[UIImageView alloc] initWithImage:picture];
CGRect screenRect = [[UIScreen mainScreen] bounds];
imageView.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imageView];
}
Original (I want something like it)
My app
If your image is cut , the reason is you are using a higher resolution image in a smaller container and instructed your imageview to fill , using this line imageView.contentMode = UIViewContentModeScaleAspectFill;
Change it to imageView.contentMode = UIViewContentModeScaleAspectFit;
I think #whitewolf09 is right and his method will solve your problem. But i suggest you to look into MGImageUtilities that will be very useful for different cases where you can crop images maintaining aspect ratio to fit inside your image view's frame.
Just #import "UIImage+ProportionalFill.h"
UIImage *image = [[UIImage imageWithContentsOfFile:filePath] imageScaledToFitSize:imageView.frame.size];
That's the very useful category for image resizing and may help you in future.
I think the reason it is being clipped is that you are creating the image view with a picture THEN setting the frame and probably the picture is bigger than the frame so it is clipping it. Also try changing the aspect to: 'UIViewContentModeScaleAspectFit' Try this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.translucent = NO;
UIImage *picture = [UIImage imageWithContentsOfFile:self.picturePath]; //I passed this path from the previous View
UIImageView* imageView = [[UIImageView alloc] initWithRect:CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);];
CGRect screenRect = [[UIScreen mainScreen] bounds];
imageView.image = picture;
imageView.contentMode = UIViewContentModeCenter;
[self.view addSubview:imageView];
}

Stopping UIScrollView at specific place while scrolling with pagingEnabled

I have the following code to create a scroll view with additional area at the beginning and the end of the images (50 points).
UIScrollView* scroll = [[UIScrollView alloc] initWithFrame: CGRectMake(0,0,200,100)];
scroll.contentSize = CGSizeMake(400,100);
UIImageView* img1 = [[UIImageView alloc] initWithFrame: CGRectMake(50,0,100,100);
UIImageView* img2 = [[UIImageView alloc] initWithFrame: CGRectMake(150,0,100,100);
UIImageView* img3 = [[UIImageView alloc] initWithFrame: CGRectMake(250,0,100,100);
//Adding images to ImageViews
scroll.pagingEnabled = YES;
[scroll addSubView:img1];
[scroll addSubView:img2];
[scroll addSubView:img3];
The first time I see the view, I will see the additional area on the left (0-50), then the first image (50-150) and then half of the second image (150-200).
When I swipe left, I want to see half of the first image on the right, the second image at the center, and half of the third image on the right.
When I swipe left again, I want to see the third image at center, with half of the second image on the left, and the additional area on the right.
Can it be possible?
It's possible as long as you define everything correctly. Make sure that the assigned content size is equal to the overall size that you wish to scroll through. Make sure that each page size is equal to the scroll view's frame. You can set the clipsToBounds to NO if the frame clips your subviews.
You can do this by adjusting the contentSize of UIScrollView
- (void)addImagesToScrollView{
//An offset from where imageViewStarts
CGFloat xOffset = 50.0f;
CGRect imageViewFrame = CGRectMake(xOffset, 0.0f, 100.0f, 100.0f);
for (NSString *imageName in #[#"image1.jpg",#"image2.jpg",#"image3.jpg"])
{
UIImageView *imageView = [[UIImageView alloc]initWithFrame:imageViewFrame];
UIImage *image = [UIImage imageNamed:imageName];
imageView.image = image;
[self.scrollView addSubview:imageView];
imageViewFrame.origin.x+=imageViewFrame.size.width;
}
CGSize contentSize = self.scrollView.frame.size;
//Content size is calculate from the imageViewFrame and an offset is added to it
contentSize.width = imageViewFrame.origin.x+xOffset;
[self.scrollView setContentSize:contentSize];
}
Source Code
You can scroll to a particular place in the scrollview using the following code
[YOURSCROLLVIEW setContentOffset:CGPointMake(x, y) animated:YES];

move didFinishLaunchingWithOptions: method to viewcontroller method with button trigger

I had the following code working in AppDelegate.h/.m but cannot change it to work in ViewController with a button triggering it.
#implementation BSViewController
#synthesize imageView;
#synthesize workingImage;
- (IBAction) chooseImage:(id) sender {
UIImage* testCard = [UIImage imageNamed:#"ipad 7D.JPG"];
CGImageRef num = CGImageCreateWithImageInRect([testCard CGImage],CGRectMake(532, 0, 104, 104));
UIGraphicsBeginImageContext(CGSizeMake( 250,650));
CGContextRef con = UIGraphicsGetCurrentContext();
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
CGContextDrawImage(con, CGRectMake(0, 0, 13, 13) ,num);
UIGraphicsEndImageContext();
CGImageRelease(num);
UIImageView* iv = [[UIImageView alloc] initWithImage:im];
[self.imageView addSubview: iv];
iv.center = self.imageView.center;
[iv release];
I see the button named "Choose image" in the simulator, but I do not see the image there.
You haven't added self.imageView to any views.
You need
[self.view addSubview:self.imageView];
To show your imageView.
I was able to make it work, but let me ask you some dummy questions before. You can skip these and jump directly to the end for the answer:
Did it worked before?
Why aren't you using ARC?
Have you linked the chooseImage: to the button with Interface Builder, or programmatically? I sometimes forget this basic step, and nothing works of course :)
Why are you adding iv as a subview of self.imageView instead of just changing the image property of self.imageView. Knowing this could be useful to answer properly.
Supposing you have a good reason for adding iv as a subview, beware iv.center = self.imageView.center the centers are in two different coordinates system.
Form the Apple Documentation:
The center is specified within the coordinate system of its superview
and is measured in points. Setting this property changes the values of
the frame properties accordingly.
Edit: Other things to check
Does self.imageView exist? I mean, have you added it with IB and linked it to the IBOutlet, or defined it programmatically?
Add a break point in chooseImage: and go through it step by step, making sure that the objects you generate there are not nil
Test: try self.imageView setImage:im instead of self.imageView addSubview:iv, even if it's not what you want to do, if everything else is ok you should see the image.
It works if you switch the order of
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
and
CGContextDrawImage(con, CGRectMake(0, 0, 13, 13) ,num);
Otherwise you would be asking to create im with the content of the context without nothing in drawn in it.
The code would then become:
UIImage* testCard = [UIImage imageNamed:#"ipad 7D.JPG"];
CGImageRef num = CGImageCreateWithImageInRect([testCard CGImage],CGRectMake(532, 0, 104, 104));
UIGraphicsBeginImageContext(CGSizeMake( 250,650));
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextDrawImage(con, CGRectMake(0, 0, 13, 13) ,num);
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRelease(num);
Hope it helps. :D

Finger Painting from a UIView Class to Another UIView Class

I have a UIViewController Class which holds 2 custom UIView classes which are:
ItemView
DrawingView
in UIViewController ViewDidLoad method I have the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
item = [[ItemView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 230, 230)];
item.opaque = NO;
[self.view addSubview:item];
drawing = [[DrawingView alloc] initWithFrame:self.view.frame];
[self.view insertSubview:drawing aboveSubview:item];
[item release];
[drawing release];
}
The DrawingView class gets UITouches and accordingly draw on the screen.
My question is:
I can draw all over the screen except for on top of ItemView class object. DrawingView class cannot draw on to ItemView.
Let me explain it in another word:
The DrawView class works on the screen except for the area of itemView subview. On top of itemView, DrawView cannot make any draws but other than itemView area, it makes drawing.
What can I do?
What can be the problem?
How can I make drawing on all screen including the itemView area added by addSubview on ViewController class?
EDIT: I am adding a screenshot in order to explain the problem better
EDIT 2: I found that the problem is related to opacity. In the ItemView class, I changed the added UIImageView object alpha value to 0.5f.
The result is semi-transparent view and now my finger drawing is visible. However, this is not what I want. I want to draw on top of the view. I do not want to play with the alpha value.
Finally, I found the answer myself. It is not exactly what I wanted but my solution is based on CALayer.
Instead of using a custom UIView class like ItemView I added the following class method in ItemView Class:
+(CALayer *)imgLayer
{
UIImage *img = [UIImage imageNamed:#"face.png"];
CALayer *layer = [CALayer layer];
CGFloat nativeWidth = CGImageGetWidth(img.CGImage);
CGFloat nativeHeight = CGImageGetHeight(img.CGImage);
CGRect startFrame = CGRectMake(0, 0, nativeWidth, nativeHeight);
layer.contents = (id)img.CGImage;
layer.frame = startFrame;
return layer;
}
In My UIVIewController Class I called my static function as following:
CAGradientLayer *bgLayer = [GradientView greyGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
CALayer *imgLayer =[ItemView imgLayer];
imgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:imgLayer above:bgLayer];
And it works! now I can make drawings on my image.

Resources