I have got a UIButton inside a UIView that I set as the cameraOverlayView.
I also scaled the uiimagepicker cameraView to cover the whole screen (as you can see below in the picture).
Here's my code :
// CameraViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if(!self.imagePickerController)
[self setupCamera];
}
-(void)setupCamera{
self.imagePickerController = [[UIImagePickerController alloc] init];
self.imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
self.imagePickerController.delegate = self;
self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.imagePickerController.showsCameraControls = NO;
self.imagePickerController.cameraOverlayView = self.overlayView;
[self scaleCameraPreview];
// Present picker in the next loop (to avoid warning - "Presenting view controllers on detached view controllers is discouraged")
[self performSelector:#selector(presentPicker) withObject:nil afterDelay:0.0];
}
-(void)presentPicker{
[self presentViewController:self.imagePickerController animated:NO completion:nil];
}
-(void)scaleCameraPreview{
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
int heightOffset = 0;
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
heightOffset = 120;
}
float cameraAspectRatio = 4.0 / 3.0;
float imageWidth = floorf(screenSize.width * cameraAspectRatio);
float scale = ceilf(((screenSize.height + heightOffset) / imageWidth) * 10.0) / 10.0;
self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);
}
-(IBAction)cameraButtonPressed:(id)sender{
[self.imagePickerController takePicture];
}
And this is my hierarchy of viewcontrollers:
- UINavigationController
- SWRevealViewController (manages a drawer functionality between different controllers)
- UINavigationController (current controller for the camera)
- CameraViewController
- PickerViewController (Presented modally as you can see above)
- PhotoTakenViewController (Controller that will fire after UIImagePicker returns an image)
EDIT: Adding overlay info
I have searched online for similar threads (e.g. UIImagePickerController Overlay Buttons Not Firing) but didn't find any solution.
The UIButton that you can see in the picture, is inside a UIView this is the overlayView in the code assigned to the cameraOverlayView. It is also connected to the cameraButtonPressed: action through an outlet, and it works perfectly if I don't add the UIImagePickerController.
It isn't responding to any touches I don't know why.
Does anyone know what's going on?
I've fixed my issue but I believe in a sketchy way:
[self presentViewController:self.imagePickerController animated:NO completion:^(void){
// Re-add the button so it is on top of the overlay
[self.imagePickerController.view addSubview:self.takePhotoButton];
}];
Will probably come back later to this issue and post an update in case I find a better solution.
Related
I am trying to achieve a modal presentation effect where the presented view covers the parent view only partially as shown in the picture below.
I know I could achieve this by implementing custom transitions using UIPresentationController. I don't want to reinvent the wheel so before I roll on with development I would like to ask.
Is there a build in support for this kind of transition in the APIs?
I researched all available Modal Presentation Styles and it appears to me there is no support for the transition I want to make and the only way of achieving it is just to code it.
I ran into this exact same issue. I went down the modal presentation styles route as well and kept hitting a wall (specifically getting it working on an iPhone rather than an iPad).
After some digging around, I was able to get it working though. Here's how I did it:
To start, we need a view controller that we will be presenting (the modal one) to set it's view's background color to transparent and set the frame of the navigation controller's view to some offset.
ModalViewController.h
#import UIKit;
#class ModalViewController;
#protocol ModalViewControllerDelegate <NSObject>
- (void)modalViewControllerDidCancel:(ModalViewController *)modalViewController;
#end
#interface ModalViewController : UIViewController
#property (weak, nonatomic) id<ModalViewControllerDelegate> delegate;
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
#end
ModalViewController.m
static const CGFloat kTopOffset = 50.0f;
#implementation ModalViewController {
UINavigationController *_navController;
}
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
rootViewController.navigationItem.leftBarButtonItem = [self cancelButton];
_navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
self.view.backgroundColor = [UIColor clearColor];
[self.view addSubview:_navController.view];
// this is important (prevents black overlay)
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect bounds = self.view.bounds;
_navController.view.frame = CGRectMake(0, kTopOffset, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - kTopOffset);
}
- (UIBarButtonItem *)cancelButton
{
return [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(cancelButtonClicked:)];
}
- (void)cancelButtonClicked:(id)sender
{
[_delegate modalViewControllerDidCancel:self];
}
#end
Next, we need to set up the presenting controller to run the following animation:
Scale itself down
Fade out a lil' bit
Present the modal view controller using presentViewController:animated:completion
This is what I did
PresentingViewController.m
static const CGFloat kTransitionScale = 0.9f;
static const CGFloat kTransitionAlpha = 0.6f;
static const NSTimeInterval kTransitionDuration = 0.5;
#interface PresentingViewController <ModalViewControllerDelegate>
#end
#implementation PresentingViewController
...
...
- (void)showModalViewController
{
self.navigationController.view.layer.shouldRasterize = YES;
self.navigationController.view.layer.rasterizationScale = [UIScreen mainScreen].scale;
UIViewController *controller = // init some view controller
ModalViewController *container = [[ModalViewController alloc] initWithRootViewController:controller];
container.delegate = self;
__weak UIViewController *weakSelf = self;
[UIView animateWithDuration:kTransitionDuration animations:^{
weakSelf.navigationController.view.transform = CGAffineTransformMakeScale(kTransitionScale, kTransitionScale);
weakSelf.navigationController.view.alpha = kTransitionAlpha;
[weakSelf presentViewController:container animated:YES completion:nil];
} completion:^(BOOL finished) {
weakSelf.navigationController.view.layer.shouldRasterize = NO;
}];
}
#pragma mark - ModalViewControllerDelegate
- (void)modalViewControllerDidCancel:(ModalViewController *)modalViewController
{
__weak UIViewController *weakSelf = self;
[UIView animateWithDuration:kTransitionDuration animations:^{
weakSelf.navigationController.view.alpha = 1;
weakSelf.navigationController.view.transform = CGAffineTransformIdentity;
[weakSelf dismissViewControllerAnimated:YES completion:nil];
}];
}
#end
pretty sure its done like this
let newVC = <view controller you want to display>
let nav: UINavigationController = UINavigationController(rootViewController: newVC)
if let currVc = UIApplication.sharedApplication().keyWindow?.rootViewController {
nav.transitioningDelegate = currVc
nav.modalPresentationStyle = UIModalPresentationStyle.Custom;
currVc.presentViewController(nav, animated: true, completion: nil)
}
I'm pretty sure this is your answer - Page sheet - as in UIModalPresentationPageSheet
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/mobilehig/Alerts.html#//apple_ref/doc/uid/TP40006556-CH14-SW3
My code to add a UIImagePickerController:
picker = [[[UIImagePickerController alloc] init] autorelease];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.showsCameraControls = NO;
[self displayContentController:picker inView:cameraBoxView];
[self.view sendSubviewToBack:picker.view];
- (void)displayContentController:(UIViewController *)content inView:(UIView *)v {
//Delete the previous instance of the same view controller
for (UIViewController *vc in self.childViewControllers) {
if ([vc isKindOfClass:[content class]]) {
[vc removeFromParentViewController];
}
}
[self addChildViewController:content];
content.view.frame = v.frame;
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];
if ([content isKindOfClass:[CCInnerNavigationController class]]) {
innerNavigationController = (CCInnerNavigationController *)content;
}
}
I have disabled all the device orientations except of portrait. But the image from camera is rotated. How to solve this issue in both iOS 6 and 7.
P.S. I understand that there are a lot of soludions but it seems they are too old because NOTHING of them works.
When you instatiate the UIImagePickerController you must also pass it a delegate. The delegate should be conform to 2 protocols UIImagePickerControllerDelegate and UINavigationControllerDelegate. The second makes possible to add some logic for rotation at runtime, implement this methods accordingly to what you want to achieve.
– navigationControllerPreferredInterfaceOrientationForPresentation:
– navigationControllerSupportedInterfaceOrientations:
I changed the following code:
imgView.image = img;
to:
imgView.image = [UIImage imageWithCGImage:img.CGImage scale:1 orientation:UIImageOrientationRight];
And now it works in both iOS 6 and 7. I don't know how it fixes this bug but it really works. Maybe image picker corrupts the image or image view and this code fixes it by creating a new image instance.
I'm trying to use a scroll view to have pagination with pages of subviews that are images that can be pinched zoomed on iOS. The pagination works, but as soon as an image is pinch-zoomed, the app crashes with EXEC_BAD_ACCESS(code=1,address=...)
I'm aware that it's a bit odd to swipe a zoomed image to pan the image and also swipe to paginate, but in the real app, the pagination will be done with a page control. Also I think it could work like the preview app. If an image is zoomed, panning will go down to the bottom of the image and then after that is reached, it goes to the next image.
Is this possible?
Here's an example:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
ScrollerViewController *viewController = [[ScrollerViewController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
return YES;
}
ScrollerViewController.m - the outer pagination view controller
- (void)viewDidLoad {
[super viewDidLoad];
// outer scroll view for paging with two pages
CGRect frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height);
UIScrollView *pagingScroller = [[UIScrollView alloc] initWithFrame:frame];
pagingScroller.pagingEnabled = YES;
pagingScroller.scrollsToTop = NO;
pagingScroller.userInteractionEnabled = YES;
pagingScroller.contentSize = CGSizeMake(self.view.bounds.size.width*2,self.view.bounds.size.height);
// first page
ImageViewController *page1 = [[ImageViewController alloc] init];
page1.filename = #"cat.jpg";
page1.view.frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height);
[pagingScroller addSubview:page1.view];
// second page
ImageViewController *page2 = [[ImageViewController alloc] init];
page2.filename = #"dog.jpg";
page2.view.frame = CGRectMake(self.view.bounds.size.width,0,self.view.bounds.size.width,self.view.bounds.size.height);
[pagingScroller addSubview:page2.view];
self.view = pagingScroller;
}
ImageViewController.m - the pinch-zoom image
- (void)viewDidLoad {
[super viewDidLoad];
// scroll view for pinch zooming
CGRect frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height);
UIScrollView *zoomScroller = [[UIScrollView alloc] initWithFrame:frame];
zoomScroller.minimumZoomScale = 1.0;
zoomScroller.maximumZoomScale = 5.0;
zoomScroller.userInteractionEnabled = YES;
zoomScroller.delegate = self;
imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.userInteractionEnabled = YES;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = [UIImage imageNamed:filename];
[zoomScroller addSubview:imageView];
self.view = zoomScroller;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imageView;
}
The full project is at https://github.com/tomkincaid/ZoomScrollTest
I can test that the pinch zoom works by changing
ScrollerViewController *viewController = [[ScrollerViewController alloc] init];
to
ImageViewController *viewController = [[ImageViewController alloc] init];
viewController.filename = #"cat.jpg";
Its been quite a while that you posted your question. I bet you fixed it already yourself but I want to make sure other people can use your code.
However I downloaded your small GitHub project and found that you get the crash because you don't retain the ImageViewController's page1 and page2 in [ScrollerViewController viewDidLoad]. The views them selfs don't retain their controllers therefor the controllers get released after viewDidLoad in your case. Then when you pinch on the image scroll view it calls for its delegate but it is already deallocated.
To fix this I added two ImageViewController properties to the ScrollerViewController class and stored the controller objects there.
#interface ScrollerViewController ()
#property (strong) ImageViewController *page1;
#property (strong) ImageViewController *page2;
#end
In [ScrollerViewController viewDidLoad] I added at the end:
self.page1 = page1;
self.page2 = page2;
I hope that someone may find this information useful. Maybe you want to update your GitHub project so that it will compile and run.
I am developing an app for iPad2 which allows user to take photos. I am going to use custom overlay for image picker. But this overlay view does not detect the orientation of the device.
I have tried with
didRotateFromInterfaceOrientation and willAnimateRotationToInterfaceOrientation. But non of them are working. This is how I am creating overlay.
#interface CameraOverlayController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
-(void)setupImagePicker:(UIImagePickerControllerSourceType)sourceType
{
self.picker.sourceType = sourceType;
if (sourceType == UIImagePickerControllerSourceTypeCamera)
{
self.picker.showsCameraControls = NO;
if ([[self.picker.cameraOverlayView subviews] count] == 0)
{
CGRect overlayViewFrame = self.picker.cameraOverlayView.frame;
if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) {
NSLog(#"Initially Landscape and height : %f" , CGRectGetHeight(overlayViewFrame));
}
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation)) {
NSLog(#"Initially Portrait and height : %f", CGRectGetHeight(overlayViewFrame));
}
self.view.backgroundColor = [UIColor clearColor];
CGRect newFrame = CGRectMake(0.0, CGRectGetHeight(overlayViewFrame) - self.view.frame.size.height - 10.0, CGRectGetWidth(overlayViewFrame), self.view.frame.size.height + 10.0);
self.view.frame = newFrame;
[self.picker.cameraOverlayView addSubview:self.view];
}
}
}
Can anyone please tell me how can I detect the rotation of the device within camera overlay?
Many thanks
I have solove above issue by myself.
The above function has been recreated like this.
-(void)setupImagePicker:(UIImagePickerControllerSourceType)sourceType
{
self.picker.sourceType = sourceType;
if (sourceType == UIImagePickerControllerSourceTypeCamera)
{
self.picker.showsCameraControls = NO;
}
}
Then I have use navigation controller delegate. Within this function I could capture the device orientation and solved my issues.
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
Thanks for viewing this.
I am interested to know on how I can resize the view when using UIModalPresentationFormSheet of modalPresentationStyle, it looks like it has a fixed size so I was wondering if anyone out there did managed to manipulate the popup view from the sizing perspective.
So there is either UIModalPresentationFormSheet with a fixed view size or full views and I am after something in between.
MyModalViewController *targetController = [[[MyModalViewController alloc] init] autorelease];
targetController.modalPresentationStyle = UIModalPresentationFormSheet;
targetController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:targetController animated:YES];
// it is important to do this after presentModalViewController:animated:
targetController.view.superview.bounds = CGRectMake(0, 0, 200, 200);
You are able to adjust the frame of a modal view after presenting it:
Tested in iOS 5.1 - 6.1, using XCode 4.62
MyModalViewController *targetController = [[[MyModalViewController alloc] init] autorelease];
targetController.modalPresentationStyle = UIModalPresentationFormSheet;
targetController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; //transition shouldn't matter
[self presentModalViewController:targetController animated:YES];
targetController.view.superview.frame = CGRectMake(0, 0, 200, 200);//it's important to do this after presentModalViewController
targetController.view.superview.center = GPointMake(roundf(self.view.center.x), roundf(self.view.center.y));//self.view assumes the base view is doing the launching, if not you might need self.view.superview.center etc.
Update The preferred iOS 6.0 view controller presentation method also works correctly:
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
In ios8 and earlier works:
AboutViewController * _aboutViewController = [[AboutViewController alloc] init];
_aboutViewController.modalPresentationStyle = UIModalPresentationFormSheet;
if(IS_IOS8)
{
_aboutViewController.preferredContentSize = CGSizeMake(300, 300);
}
[self presentViewController:_aboutViewController animated:YES completion:nil];
In AboutViewController.m
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
if(!IS_IOS8)
{
self.view.superview.bounds = CGRectMake(0, 0, 300, 300);
}
}
IS_IOS8
#define IS_IOS8 ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8)
In iOS 8 you can also use UIPresentationController which gives you more customization options.
For iOS 8, simply implement the delegate method (CGSize)preferredContentSize on each view controller. It should resolve all the size issue.
Just to extent Fatos solution (great one)
If your are creating the view controller using a .xib file after the alloc initWithNibName you could store the view frame:
CGRect myFrame = targetController.view.frame;
...
targetController.view.superview.bounds = myFrame;
And then use it for superview.bounds, so the view's size in the .xib will be used and you could change the size more visually.
I put this code in the form sheet view controller:
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.view.superview.bounds = CGRectMake(0, 0, 540, 500); // your size here
}
Note that the resizing occurs early enough that the the presentation animation looks correct.