I would like my GPUImageView (portrait, front camera) to be fullscreen whatever the phone screen size (from iPhone 4 to Iphone 6+). Ideally following an "Aspect fill" mode.
Right now I have :
_vidCamera = [[GPUImageVideoCamera alloc]
initWithSessionPreset:AVCaptureSessionPreset640x480
cameraPosition:AVCaptureDevicePositionBack];
_vidCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
// View
CGRect f = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
_filteredVideoView = [[GPUImageView alloc] initWithFrame:f];
Would kGPUImageFillModeStretch be enough ? Do I need to change the SessionPreset as well ? If I change the Session preset to something bigger than say the iPhone 4 can handle, will it cause trouble ?
Sorry I can't test it myself because I don't own more than an iPhone 4.
There are 3 mode in the source code.
public var kGPUImageFillModeStretch: GPUImageFillModeType { get }
// Stretch to fill the full view, which may distort the image outside of its normal aspect ratio
public var kGPUImageFillModePreserveAspectRatio: GPUImageFillModeType { get }
// Maintains the aspect ratio of the source image, adding bars of the specified background color
public var kGPUImageFillModePreserveAspectRatioAndFill: GPUImageFillModeType { get }
// Maintains the aspect ratio of the source image, zooming in on its center to fill the view
In my opinion, It is better to use kGPUImageFillModePreserveAspectRatioAndFill. Using the stretch option causes weird outputs.
To set the fillMode to your GPUImageFilter:
filterView.fillMode = kGPUImageFillModePreserveAspectRatioAndFill
Related
I have this AVFoundation camera app of mine. The camera preview is the result of a filter, applied by didOutputSampleBuffer method.
When I setup the camera I am following what apple did on one of their sample codes (CIFunHouse):
// setting up the camera
CGRect bounds = [self.containerOpenGL bounds];
_eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
_videoPreviewView = [[GLKView alloc] initWithFrame:bounds
context:_eaglContext];
[self.containerOpenGL addSubview:_videoPreviewView];
[self.containerOpenGL sendSubviewToBack:_videoPreviewView];
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
NSDictionary *options = #{kCIContextUseSoftwareRenderer : #(NO),
kCIContextPriorityRequestLow : #(YES),
kCIContextWorkingColorSpace : [NSNull null]};
_ciContext = [CIContext contextWithEAGLContext:_eaglContext options:options];
[_videoPreviewView bindDrawable];
_videoPreviewViewBounds = CGRectZero;
_videoPreviewViewBounds.size.width = _videoPreviewView.drawableWidth;
_videoPreviewViewBounds.size.height = _videoPreviewView.drawableHeight;
dispatch_async(dispatch_get_main_queue(), ^(void) {
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
_videoPreviewView.transform = transform;
_videoPreviewView.frame = bounds;
});
self.containerOpenGL is a full screen view and is constrained to the four corners of the screen. Autorotation is on.
But this is the problem...
When I setup the GLKView and self.ciContext it is created assuming the device is on a particular orientation. If the device is on a particular orientation and I run the application, the previewView will fit the entire self.containerOpenGL area but when I rotate the device the previewView will be out center.
I see that Apple code works perfectly and they don't use any constraints. They do not use any autorotation method, no didLayoutSubviews and when you rotate the device, running their code, everything rotates except the preview view. And worse than that, my previewView appears to rotate but not their's.
Is this black magic? How do I they do that?
They add their preview view to a uiwindow and that is why it does not rotate. I hope this answers the question. If not I will continue to look through their source code.
Quote from source code.
we make our video preview view a subview of the window, and send it to the back; this makes FHViewController's view (and its UI elements) on top of the video preview, and also makes video preview unaffected by device rotation
They also add this
_videoPreviewView.enableSetNeedsDisplay = NO;
This may keep it from responding as well
Edit: It appears that now the preview rotates and the UI does as well so to combat this you can add a second window and send it to the back and make the main window clear and add the previewView to the second window with a dummyViewController that tells it not to autorotate by overriding the appropriate method. This will allow the preview to not rotate but the UI to rotate.
There's probably a dozen SO questions with similar titles, and as far as I can see, I've incorporated the advice from every one of them, with no luck. When I rotate the device from portrait to landscape, I want to change the background image. However, in every experiment I have tried, the UIImageView in landscape remains portrait sized.
I am using Autolayout, configured in IB like so:
My view hierarchy is configured like so:
When I rotate the device, I want to rotate everything in UIView viewBackground (the image and all of the buttons). I believe I can manage the button movements through constraints, but need to rotate UIImageView image view background myself. So, I added the following code:
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
int scaleFactor = self.traitCollection.displayScale;
NSString *source = #"drawn";
NSString *orientation;
if (size.width > size.height)
{
orientation = #"Landscape";
}
else
{
orientation = #"Portrait";
}
NSString *platform;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
platform = #"iPad";
}
else
{
platform = #"iPhone";
}
NSString *filename = [NSString stringWithFormat:#"%# %# %# %.0fx%.0f.png", source, platform, orientation, size.width * scaleFactor, size.height * scaleFactor];
// CONFIRMED: we have assembled the correct new image name.
viewBackground.frame = CGRectMake(0, 0, size.width * scaleFactor, size.height * scaleFactor);
viewBackground.autoresizesSubviews = YES;
viewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.autoresizesSubviews = YES;
imageViewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.image = [UIImage imageNamed:filename];
}
The problem: The correct image appears on the screen, but as as mentioned above, it still has the portrait dimensions. I'm certain the image changes as the artwork is significantly different, with elements being stacked horizontally versus vertically. But the new picture is cut off horizontally in landscape mode.
I have tried putting all sorts of wild values into newRect, and none of them make any difference. The image never actually moves, even if I radically change the origin in newRect. so my current theory is that my changes to the view sizes are being ignored/over written.
The Question: What am I missing that's preventing the resizing of the image to landscape mode? Is there some other auto sizing aspect I'm not addressing?
So, the problem turned out to be yet another variable in the saga. There also must be a constraint on the UIImageView that is 0 on each side, with "Constrain to Margins unchecked." Apparently, when I did that step earlier, it didn't actually take.
I have to admit, though, I really don't understand what that constraint is doing for me. It clearly makes the rotation work, but I don't get why. Since that's technically the answer to my original question, I'll gladly award the answer if someone can explain how this constraint enables this scenario.
EDIT:
In case anyone stumbles upon this answer in the future... I see now that setting the aforementioned constraint with all 0 buffers between the UIImageView and the UIView pins the edges of UIImageView to the edges of the UIView. When the device rotates, the viewController resizes the UIView, and with this constraint, the UIImageView resizes also. I was able to remove the majority of my code. The only code I need is to select a portrait or landscape image, and I was able to remove everything that resized the UIView and UIImageView.
The image now changes size properly when rotating just by using the one constraint. I only need code to decide whether to show a landscape or portrait background. (And I vastly simplified that code by storing my images in an asset catalog so the code just selects the name of "portrait" or "landscape", but the size of the image is auto selected based on the device).
I have a really simple App, with one view, one button with one activity when you press this button. The button is full screen size, as well as the image that is in it.
I have it running on my iPhone 6 Plus perfectly, after spending a long time getting the Controller View, View Container, and Button sizes to work together so it is seen properly on the iPhone 6 plus, both hardware and virtual.
The button and image are full screen size, and it took some fenagling to get even this to work for ONE iPhone size. LOL. The Controller View is one size, which is NOT the real life size of my iPhone 6 plus. It is larger. So the View Container is the correct size, along with the button. The image is PNG 401PPI 1080 x 1920 as Apple recommends for the iPhone 6 plus. It looks correct on my iPhone.
My issue is the following: How do I now take my App, and create the other two sizes for the iPhone 5 and iPhone 4?
I have scoured here and on the Apple Developer website to absolutely no avail whatsoever. Nothing I have tried so far, has done anything but make me pull my hair.
I am using XCode 6.4 and targeting iOS 8 and above.
Here is what I have set within the XCode environment:
iPhone 6 plus layout details:
a. View Controller attributes:
- resize view from NIB
- Presentation : Fullscreen
- Simulated metrics iPhone 5.5 inch, fixed size (seems too big, the image is not fullsize to this size) 414 x 736
b. ViewContainer
- Mode: scale to fill (scales to View Controller above)
c. Button
- Image = iOS 401PPI.png
- Alignment = Centered horizontal and vertical
- Edge = Content
- View = Scale to Fill
— Size Inspector
View = Width 375 x Height 652
Intrinsic Size = Default
screen actual size = 667 x 375 “ViewContainer”
button size = 652 x 375 “button”
Image is original 401ppi 1080 x 1920 # 401 ppi
HELP...
iPhone and iOS system uses smart naming system to select the appropriate resolution image based on the device the image is being rendered on.
Back then, an iPhone 3 or 3gs has a screen dimension of 320x480 pixels. In this environment, if we wanted a button that is 100 x 50 pixels, then we created one of that size in Photoshop.
With the introduction of the Retina displays, the screen pixel resolution is doubled, but Apple wants to make it easier for the developer so with our Objective C code, we still tell our button to be 100x50 dimension but iOS measures it in points instead of pixels.
That way, on Retina screen, 100x50 points is converted to 200x100 pixels.
iOS will also then try to find an image with a naming suffix "#2x" and load this one if it's available.
Demo
So suppose we got this public domain eggs image here:
http://www.publicdomainpictures.net/view-image.php?image=14453&picture=easter-eggs-in-grass&large=1
Which I've resized to 600x900 for the eggs#3x.png iPhone 6/6+ image.
Then the we have:
eggs#3x.png 600x900 pixels
eggs#2x.png 400x600 pixels
eggs.png 200x300 pixels
Create a new Single View Template project in Xcode. Drag and drop all three eggs images to the Image.xcassets file manager. Then modify the ViewController file to look like this:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (nonatomic, strong) UIImageView *imageView;
#end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self initViews];
[self initConstraints];
}
-(void)initViews
{
self.imageView = [[UIImageView alloc] init];
// ---------------------------------------------------------------
// extension not needed since we added images to the
// Images.xcassets manager instead of directly to project
// ---------------------------------------------------------------
self.imageView.image = [UIImage imageNamed:#"eggs"];
// ---------------------------------------------------------------
// image might not cover the imageView's frame, I'm using
// a grey background color so you can see where the frame is
// ---------------------------------------------------------------
self.imageView.backgroundColor = [UIColor grayColor];
// ---------------------------------------------------------------
// images may or may not be square, we use AspectFit
// to ensure we can see the entire image within the
// dimension we specified without any cropping
// ---------------------------------------------------------------
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
// add the view to the view controller's view
[self.view addSubview:self.imageView];
}
-(void)initConstraints
{
// tell iOS we want to use Autolayout for our imageView
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
// ---------------------------------------------
// name binding for our imageView for
// Autolayout Visual Formatting Language
// ---------------------------------------------
NSMutableDictionary *viewNames = [[NSMutableDictionary alloc] init];
[viewNames setValue:self.imageView forKey:#"imageView"];
// ------------------------------------------------------------------
// Autolayout code
//
// H: for horizontal constraint
// V: for vertical constraint
// |: parent view edges (so H:| == left edge for example)
//
//
// Here we're telling our imageView to be offset 50 pixels
// from the left and right as well as 50 pixels from the top
// and bottom of its parent view, which in this case, is our
// View Controller's view
// ------------------------------------------------------------------
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-50-[imageView]-50-|"
options:0
metrics:nil
views:viewNames]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[imageView]-50-|"
options:0
metrics:nil
views:viewNames]];
}
Unfortunately, Xcode no longer comes with iPhone 3/3gs simulator, only retina iPhones.
iPhone 4/4s simulator result
iPhone 5/5s simulator result
iPhone 6 simulator result
Notice how our imageView's frame remains 50 pixels offset on all four sides, relative to the view controller's view ?
If you want the image to fill the entire ImageView's frame, you can use the UIViewContentModeScaleAspectFill instead of UIViewContentModeScaleAspectFit in the above code.
This yields a result like this:
So in the case of your button image, you would do something similar.
Thank you Zhang.
I went ahead and created a new asset catalog with three images, one for each size of iPhone, and then simply dragged the images into that folder in the XCode editor window.
The contents.json asset catalog file looks like this:
Contents.json :
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "MainImage.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "MainImage#2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "MainImage#3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
With the exception that I am using Swift which makes this less code and therefore simpler, your answer is spot on. In Swift, there is no more need for two files for the ViewController, only one. I also was able to assign my sound to a single button within the ViewController, removing the need for any ImageView container for my background image:
ViewController :
import UIKit
import AVFoundation
class ViewController: UIViewController {
var myPlayer = AVAudioPlayer()
var yourSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("RemSound_01", ofType: "wav")!)
func initYourSound() {
myPlayer = AVAudioPlayer(contentsOfURL: yourSound, error: nil)
myPlayer.prepareToPlay()
}
override func viewDidLoad() {
super.viewDidLoad()
initYourSound()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playMySound(sender: AnyObject) {
myPlayer.play()
}
}
I hope this helps anyone starting out coding with Swift in XCode.
In my application, i need the user to take a snap of only a 10 letter word (using overlay, which should be right in the centre of the screen of the UIImagePicker), and then in need to show him that image (only the part of the image covered by that rectangle). So, I need to crop that image according to the overlay.
Here, i have taken a picture using UIImagePickerControl. Now, i want to see the dimensions of the image that i have taken..
UIImage *imageToprocess = [info objectForKey:UIImagePickerControllerOriginalImage];
NSLog(#"image width %f", imageToprocess.size.width);
NSLog(#"image height %f", imageToprocess.size.height);
I see the following result on console.. But how is this possible. the dimensions of the image is exceeding the dimension of the iPhone screen size.. (which is 320, 568)
UsingTesseractOCR[524:60b] image width 2448.000000
2013-12-17 16:02:18.962 UsingTesseractOCR[524:60b] image height 3264.000000
Can anybody help me out here?? I have gone through several questions here, but did not understand how to do it.
Please help..
Refer this sample code for image capturing and cropping.
https://github.com/kishikawakatsumi/CropImageSample
For creating overlay, first create a custom view (of full dimensions of camera preview) and add an transparent image with just a rectangle in its background. use this view as overlay view.
myview =[[UIImageView alloc]init];
myview.frame=CGRectMake(0, 0, 320, 431);
// why 431? bcoz height = height of device - height of tabbar present in the
bottom for camera controls of picker
//for iphone 4 ,480-49
myview.backgroundColor =[UIColor clearColor];
myview.opaque = NO;
myview.image =[UIImage imageNamed:#"A45Box.png"];
myview.userInteractionEnabled =YES;
note that you create a background image appropriately (means dimensions). You can also draw rectangle programmatically but this is much easy way.
Secondly, talking about your cropping issue, you have to get your hands dirty....Try these links for help
https://github.com/iosdeveloper/ImageCropper
https://github.com/barrettj/BJImageCropper
https://github.com/ardalahmet/SSPhotoCropperViewController
I have an UIView that contains a UIImageView. The UIImageViews works like the branding logo of the app. When I rotate the device, the containing UIView resizes itself to correspond to the landscape or portrait proportions of the screen.
What I'm trying to achieve is to have the UIImageView scaled accordingly, keeping proportions also on the left margin.
This is the actual code for the top white "banner":
UIView *topBanner = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, height_topBanner)];
[topBanner setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin)];
[topBanner setBackgroundColor:[UIColor whiteColor]];
topBanner.autoresizesSubviews = YES;
// the logo
UIImage *topBanner_logo = [UIImage imageNamed:#"logo.png"];
float logoAspectRatio = topBanner_logo.size.width/topBanner_logo.size.height;
topBanner_logoView = [[UIImageView alloc] initWithFrame:CGRectMake(topBanner.frame.size.width/100*3, topBanner.frame.size.height/100*7, (topBanner.frame.size.height/100*86)*logoAspectRatio, topBanner.frame.size.height/100*86)];
[topBanner_logoView setImage:topBanner_logo];
topBanner_logoView.backgroundColor = [UIColor blueColor];
topBanner_logoView.contentMode = UIViewContentModeScaleAspectFit;
[topBanner_logoView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleRightMargin)];
[topBanner addSubview:topBanner_logoView];
[self.view addSubview:topBanner];
This is my starting point: portrait iPad on startup:
This is what happens when I rotate it in landscape:
As you can see, the proportions of the UIImage are ok, but I'm getting extra borders (I set the background color of the UIImageView to highlight it) because the UIImageView stretches itself to follow the change of the size of its container, and the UIImage is fit into the UIImageView and put on its center.
The same - reversed - happens when I start the app directly in landscape mode:
Then I rotate it:
... and I get the logo with extra borders on top and bottom.
I do see that I can write a function to recalculate every size on each rotation change, but I'm asking to myself if is there a way to set the UIImageView and the UIImage to make it works without hacking the autorotate/resize procedures of iOS. It sounds so simple!
You can solve this by not using UIViewContentModeScaleAspectFit, and instead calculating the aspect ratio of the image and using that to explicitly the width or height based on the other (width or height).
e.g. I rotate to landscape, and so I want the height to be 80% of the view.
CGFloat w = logo.image.size.width;
CGFloat h = logo.image.size.height;
CGFloat a = w / h;
CGFloat h_use = self.view.height *0.8;
CGFloat w_use = h_use*a;
Furthermore, set the content mode to UIViewContentModeScaleAspectFill instead now that you've explicitly set the aspect ratio.
You have set the auto resizing mask to flexible height and width:
[topBanner_logoView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleRightMargin)];
If you do not do that, the default is that the view will not chance size, and therefore, the image will not either.
I think it is because of topBanner_logoView.contentMode = UIViewContentModeScaleAspectFit;
Try topBanner_logoView.contentMode = UIViewContentModeCenter or topBanner_logoView.contentMode = UIViewContentModeLeft to prevent the UIImageView's image from resizing (and getting padding).
If the UIImageView is resizing, remove the autoresizing mask.