UnifeyeMobileViewController glView camera view is not rotating in iPhone - iphone

in my iPhone application, I am using metaio sdk with UnfieyeMobileViewController. I have customized the view to show the camera view on the EAGLView component, and its works fine. The issue the when rotate the device, the camera orientation is not changing. And also, the captured image is showing upside down. Here is my code, please check:
In ViewDidAppear:
UIInterfaceOrientation interfaceOrientation = self.interfaceOrientation;
[self willAnimateRotationToInterfaceOrientation:interfaceOrientation duration:0];
[super viewDidAppear:animated];
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
// adjust the rotation based on the interface orientation
switch (interfaceOrientation) {
case UIInterfaceOrientationPortrait:
self.glView.transform = CGAffineTransformMakeRotation(0);
break;
case UIInterfaceOrientationPortraitUpsideDown:
self.glView.transform = CGAffineTransformMakeRotation(M_PI);
break;
case UIInterfaceOrientationLandscapeLeft:
self.glView.transform = CGAffineTransformMakeRotation(M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
self.glView.transform = CGAffineTransformMakeRotation(-M_PI_2);
break;
}
// make sure the screen bounds are set correctly
CGRect mainBounds = [UIScreen mainScreen].bounds;
if( UIInterfaceOrientationIsLandscape(interfaceOrientation) )
{
int width = mainBounds.size.width;
int height = mainBounds.size.height;
mainBounds.size.width = height;
mainBounds.size.height = width;
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
// for iPhone the aspect ratio does not fit, so let's correct it
if( UIInterfaceOrientationIsLandscape(interfaceOrientation) )
{
mainBounds.size.height = 360; // because our renderer is a bit larger
mainBounds.origin.y = -20;
}
else
{
mainBounds.size.width = 360; // because our renderer is a bit larger
mainBounds.origin.x = -20;
}
}
self.glView.frame = mainBounds;
}

Related

ios face detection fails in landscape mode

I am using openCv framework for detection of face. I have blocked auto rotation. I am using this method to get face.
detectMultiScale(frame_gray, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(100, 100));
When i hold iPhone in portrait mode face detection working fine but when i rotate iPhone to Landscape mode face detection fails.
This the implantation of CvVideoCamera
self.videoCamera = [[CvVideoCamera alloc] initWithParentView:self.imageView];
self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
self.videoCamera.defaultFPS = 30;
self.videoCamera.grayscaleMode = NO;
self.videoCamera.delegate = self;
Quick fix
Before initialize the CVVideoCamera add these these lines
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
self.videoCamera = [[CvVideoCamera alloc] initWithParentView:self.previewImage];
self.videoCamera.delegate = self;
Finally i found the solution.
Need to rotate cv::Mat to portrait orientation.
Here is the my code.
self.videoCamera is the property and also add file "haarcascade_frontalface_alt.xml" to the project.
//CvVideoCamera camera initialization.
- (void)viewDidLoad
{
NSString *faceCascadePath = [[NSBundle mainBundle] pathForResource:#"haarcascade_frontalface_alt" ofType:#"xml"];
if(!face_cascade.load([faceCascadePath UTF8String])) {
NSLog(#"Could not load face classifier!");
}
self.videoCamera = [[CvVideoCamera alloc] initWithParentView:self.imageView];
self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
self.videoCamera.defaultFPS = 30;
self.videoCamera.grayscaleMode = NO;
self.videoCamera.delegate = self;
[self.videoCamera start];
}
//detect's the face in cv::Mat and displays rect around face.
bool detectAndDisplay( Mat frame )
{
BOOL bFaceFound = false;
vector<cv::Rect> faces;
Mat frame_gray;
cvtColor(frame, frame_gray, CV_BGRA2GRAY);
equalizeHist(frame_gray, frame_gray);
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(100, 100));
for(unsigned int i = 0; i < faces.size(); ++i) {
rectangle(frame, cv::Point(faces[i].x, faces[i].y),
cv::Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
cv::Scalar(0,255,255));
bFaceFound = true;
}
return bFaceFound;
}
//CvVideoCamera delegate
- (void)processImage:(Mat&)image;
{
Mat tmpMat;
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
BOOL isInLandScapeMode = NO;
BOOL rotation = 1;
//Rotate cv::Mat to the portrait orientation
if(orientation == UIDeviceOrientationLandscapeRight)
{
isInLandScapeMode = YES;
rotation = 1;
}
else if(orientation == UIDeviceOrientationLandscapeLeft)
{
isInLandScapeMode = YES;
rotation = 0;
}
else if(orientation == UIDeviceOrientationPortraitUpsideDown)
{
cv::transpose(image, tmpMat);
cv::flip(tmpMat, image, rotation);
cv::transpose(image, tmpMat);
cv::flip(tmpMat, image, rotation);
cvtColor(image, image, CV_BGR2BGRA);
cvtColor(image, image, CV_BGR2RGB);
}
if(isInLandScapeMode)
{
cv::transpose(image, tmpMat);
cv::flip(tmpMat, image, rotation);
cvtColor(image, image, CV_BGR2BGRA);
cvtColor(image, image, CV_BGR2RGB);
}
detectAndDisplay(image);
if(isInLandScapeMode)
{
cv::transpose(image, tmpMat);
cv::flip(tmpMat, image, !rotation);
cvtColor(image, image, CV_BGR2RGB);
}
else if(orientation == UIDeviceOrientationPortraitUpsideDown)
{
cv::transpose(image, tmpMat);
cv::flip(tmpMat, image, !rotation);
cv::transpose(image, tmpMat);
cv::flip(tmpMat, image, !rotation);
cvtColor(image, image, CV_BGR2RGB);
}
}

iphone's rotated view doesn't take up whole screen

I rotated a view using CGAffineTransformMakeRotation. ( iPhone - allow landscape orientation on just one viewcontroller )
As you can see below, the images have white region in left and right.
I want the image take up the whole space with black background.(at least in one dimension, width or height )
Below is the full code
- (void) viewDidLoad
{
[super viewDidLoad];
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view.backgroundColor = [UIColor blackColor];
self.imageView.backgroundColor = [UIColor blackColor];
self.imageView.opaque = NO;
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.imageView setImageWithURL:[NSURL URLWithString:self.jsonAlbumImage.url_image
relativeToURL: [NSURL URLWithString:#URL_BASE]]
placeholderImage: [GlobalHelper placeHolderImage]];
[self.view addSubview: self.imageView];
}
- (void)didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[notification object] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / 2.0)];
} else if (orientation == UIDeviceOrientationLandscapeRight) {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI)];
} else if (orientation == UIDeviceOrientationPortrait) {
[self.view setTransform:CGAffineTransformMakeRotation(0.0)];
}
}
-- EDIT --
What worked for me in the end .. don't know why modification does work.. any explanation would be great!
UIDeviceOrientation orientation = [[notification object] orientation];
CGAffineTransform t;
CGRect rect;
if (orientation == UIDeviceOrientationLandscapeLeft) {
t = CGAffineTransformMakeRotation(M_PI / 2.0);
rect = CGRectMake(0,0,480,320);
} else if (orientation == UIDeviceOrientationLandscapeRight) {
t = CGAffineTransformMakeRotation(M_PI / -2.0);
rect = CGRectMake(0,0,480,320);
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
t = CGAffineTransformMakeRotation(M_PI);
rect = CGRectMake(0,0,320,480);
} else if (orientation == UIDeviceOrientationPortrait) {
t = CGAffineTransformMakeRotation(0.0);
rect = CGRectMake(0,0,320,480);
}
else
return; // looks like there are other orientations than the specified 4
[self.view setTransform:t];
self.view.bounds = rect;
In - (void)didRotate:(NSNotification *)notification, you need to relayout your views so that they occupy all the space available. You could do something like this:
- (void)didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[notification object] orientation];
CGAffineTransform t;
if (orientation == UIDeviceOrientationLandscapeLeft) {
t = CGAffineTransformMakeRotation(M_PI / 2.0);
} else if (orientation == UIDeviceOrientationLandscapeRight) {
t = CGAffineTransformMakeRotation(M_PI / -2.0);
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
t = CGAffineTransformMakeRotation(M_PI);
} else if (orientation == UIDeviceOrientationPortrait) {
t = CGAffineTransformMakeRotation(0.0);
}
CGPoint screenCenter = CGPointMake([UIScreen mainScreen].bounds.width/2,[UIScreen mainScreen].bounds.height/2);
self.view.center = CGPointApplyAffineTransform(screenCenter, t);
self.view.bounds = CGRectApplyAffineTransform([UIScreen mainScreen].bounds, t);
[self.view setTransform:t];
}
Where :
CGRectApplyAffineTransform
Applies an affine transform to a rectangle.
CGRect CGRectApplyAffineTransform (
CGRect rect,
CGAffineTransform t
);
Try also removing this line:
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
You don´t need it if you are not going to use autorotation and I fear it might conflict with your own setting the view's frame. This is just an hypothesis to account for the fact that you are not seeing the view´s frame change.
EDIT:
I'd very much like to know what this transform thing does
well, the transform is doing the rotation.
rotating is relative to an anchor point which is used as a pivot; what happens is that if the anchor point is not in the middle of the view being rotated, then the view is also translated (imagine a rotation around a vertex).
So, it is correct to set the bounds to make things even; indeed I was suggesting just that with the lines:
self.view.center = CGPointApplyAffineTransform(screenCenter, t);
self.view.bounds = CGRectApplyAffineTransform([UIScreen mainScreen].bounds, t);
but possibly the idea of applying the same transform to both the center and the bounds was not blessed. (that is also why I asked for some traces, to see what was happening :-)

Scaling UIImageView inside UIScrollView with maintaining the rotation

I have a problem in scaling the uiimageview which is placed inside the uiscrollview. I have googled and checked all the questions related to my problem in StackOverflow as well. I tried all the answers that are posted in the StackOverflow also. Nothing worked for me.
First I am placing the uiimageview inside uiscrollview in nib file and I am taking the image from Camera roll and filling the image view. Then I am using uirotationgesturerecognizer to rotate the image.
Here is the code that I am trying to do.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#",[[UIDevice currentDevice] model]);
// Do any additional setup after loading the view, typically from a nib.
self.imagePicker = [[[UIImagePickerController alloc] init] autorelease];
self.picChosenImageView.layer.shouldRasterize = YES;
self.picChosenImageView.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.picChosenImageView.layer.contents = (id)[UIImage imageNamed:#"test"].CGImage;
self.picChosenImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.picChosenImageView.layer.shadowOpacity = 0.8f;
self.picChosenImageView.layer.shadowRadius = 8;
self.picChosenImageView.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.picChosenImageView.bounds].CGPath;
UIRotationGestureRecognizer *rotationRecognizer = [[[UIRotationGestureRecognizer alloc]initWithTarget:self
action:#selector(handleRotate:)] autorelease];
rotationRecognizer.delegate = self;
[self.picChosenImageView addGestureRecognizer:rotationRecognizer];
self.containerView.delegate = self;
self.containerView.contentSize = self.picChosenImageView.layer.frame.size;
self.containerView.maximumZoomScale = 4.0f;
self.containerView.minimumZoomScale = 1.0f;
angle = 0.0f;
useRotation = 0.0;
isRotationStarted=FALSE;
isZoomingStarted = FALSE;
}
-(void)lockZoom
{
maximumZoomScale = self.containerView.maximumZoomScale;
minimumZoomScale = self.containerView.minimumZoomScale;
self.containerView.maximumZoomScale = 1.0;
self.containerView.minimumZoomScale = 1.0;
self.containerView.clipsToBounds = false;
self.containerView.scrollEnabled = false;
}
-(void)unlockZoom
{
self.containerView.maximumZoomScale = maximumZoomScale;
self.containerView.minimumZoomScale = minimumZoomScale;
self.containerView.clipsToBounds = true;
self.containerView.scrollEnabled = true;
}
#pragma mark - ScrollView delegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.picChosenImageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
CGRect frame = self.picChosenImageView.frame;
frame.origin = CGPointZero;
self.picChosenImageView.frame = frame;
//self.picChosenImageView.transform = prevTransform;
}
-(void) scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
if(!isZoomingStarted)
{
self.picChosenImageView.transform = CGAffineTransformRotate(self.picChosenImageView.transform, angle);
NSLog(#"The zooming started");
isZoomingStarted = TRUE;
CGSize contentSize = self.containerView.bounds.size;
CGRect contentFrame = self.containerView.bounds;
NSLog(#"frame on start: %#", NSStringFromCGRect(contentFrame));
NSLog(#"size on start: %#", NSStringFromCGSize(contentSize));
//prevTransform = self.picChosenImageView.transform;
}
}
-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
if(isZoomingStarted)
{
self.picChosenImageView.transform = CGAffineTransformRotate(self.picChosenImageView.transform, angle);
isZoomingStarted = FALSE;
CGSize contentSize = self.containerView.contentSize;
CGRect contentFrame = self.containerView.bounds;
NSLog(#"frame on end: %#", NSStringFromCGRect(contentFrame));
NSLog(#"size on end: %#", NSStringFromCGSize(contentSize));
}
}
#pragma mark - GestureRecognizer methods
- (void) handleRotate:(UIRotationGestureRecognizer *)recognizer
{
if(isZoomingStarted == FALSE)
{
if([recognizer state] == UIGestureRecognizerStateBegan)
{
angle = 0.0f;
[self lockZoom];
}
useRotation+= recognizer.rotation;
while( useRotation < -M_PI )
{
useRotation += M_PI*2;
}
while( useRotation > M_PI )
{
useRotation -= M_PI*2;
}
NSLog(#"The rotated value is %f",RADIANS_TO_DEGREES(useRotation));
self.picChosenImageView.transform = CGAffineTransformRotate([self.picChosenImageView transform],
recognizer.rotation);
[recognizer setRotation:0];
if([recognizer state] == UIGestureRecognizerStateEnded)
{
angle = useRotation;
useRotation = 0.0f;
isRotationStarted = FALSE;
self.containerView.hidden = NO;
//prevTransform = self.picChosenImageView.transform;
[self unlockZoom];
}
}
}
My problem is, I am able to successfully do a zoom in and zoom out. I am able to rotate the uiimageview as I wanted to. After rotating the uiimageview to a certain angle, and when I am trying to zoom in, the imageview gets back to the original position (rotate itself back to zero degree) and then the zooming happens. I want to retain the rotation and also zoom. I tried saving the previous transform and assign in back scrollDidzoom and scrollDidBegin delegate methods. None worked. Please help me to spot my mistake which I am overlooking.
try using CGAffineTransformScale instead of just resizing the frame for zooming:
anImage.transform = CGAffineTransformScale(anImage.transform, 2.0, 2.0);
changing the transform for scaling might fix your rotation issue.
hope this helps.
I had the same problem. UIScrollView is taking control over UIImageView and it is using transform without rotation.
So I do not give image reference to scroll and I have added UIPinchGestureRecognizer for scaling.
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return nil
}
Dragging is still working :)
// viewDidLoad
var pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinchRecogniezed))
scrollView.addGestureRecognizer(pinchGestureRecognizer)
func pinchRecogniezed(sender: UIPinchGestureRecognizer) {
if sender.state == .Began || sender.state == .Changed {
let scale = sender.scale
imageView.transform = CGAffineTransformScale(imageView.transform, scale, scale)
sender.scale = 1
}
}

Need to modify image before displaying on AVCaptureVideoPreviewLayer

I'm using AVCaptureSession to capture video from my iPhone 3G and I need to capture the image and change it before displaying on my AVCaptureVideoPreviewLayer. In my current implementation, I am simply implementing the captureOutput: method and displaying the UIImage in a UIImageView, but that does not work for some reason.
Any ideas? Subclass AVCaptureSession somehow?
Thanks!
Check out this https://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html
I think it does this.
It draws face boxes onto faces.
- (void)drawFaceBoxesForFeatures:(NSArray *)features forVideoBox:(CGRect)clap orientation:(UIDeviceOrientation)orientation// videoMirrored:(const BOOL)ISVIDEOMIRRORED
{
NSArray *sublayers = [NSArray arrayWithArray:[previewLayer sublayers]];
NSInteger sublayersCount = [sublayers count], currentSublayer = 0;
NSInteger featuresCount = [features count], currentFeature = 0;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// hide all the face layers
for ( CALayer *layer in sublayers ) {
if ( [[layer name] isEqualToString:#"FaceLayer"] )
[layer setHidden:YES];
}
if ( featuresCount == 0 || !detectFaces ) {
[CATransaction commit];
return; // early bail.
}
CGSize parentFrameSize = [previewView frame].size;
NSString *gravity = [previewLayer videoGravity];
//BOOL isMirrored = isVideoMirrored;
#warning Deprecated
const BOOL isMirrored = [previewLayer isMirrored]; // deprecated
CGRect previewBox = [SquareCamViewController videoPreviewBoxForGravity:gravity
frameSize:parentFrameSize
apertureSize:clap.size];
for ( CIFaceFeature *ff in features ) {
// find the correct position for the square layer within the previewLayer
// the feature box originates in the bottom left of the video frame.
// (Bottom right if mirroring is turned on)
CGRect faceRect = [ff bounds];
// flip preview width and height
CGFloat temp = faceRect.size.width;
faceRect.size.width = faceRect.size.height;
faceRect.size.height = temp;
temp = faceRect.origin.x;
faceRect.origin.x = faceRect.origin.y;
faceRect.origin.y = temp;
// scale coordinates so they fit in the preview box, which may be scaled
CGFloat widthScaleBy = previewBox.size.width / clap.size.height;
CGFloat heightScaleBy = previewBox.size.height / clap.size.width;
faceRect.size.width *= widthScaleBy;
faceRect.size.height *= heightScaleBy;
faceRect.origin.x *= widthScaleBy;
faceRect.origin.y *= heightScaleBy;
if ( isMirrored )
faceRect = CGRectOffset(faceRect, previewBox.origin.x + previewBox.size.width - faceRect.size.width - (faceRect.origin.x * 2), previewBox.origin.y);
else
faceRect = CGRectOffset(faceRect, previewBox.origin.x, previewBox.origin.y);
CALayer *featureLayer = nil;
// re-use an existing layer if possible
while ( !featureLayer && (currentSublayer < sublayersCount) ) {
CALayer *currentLayer = [sublayers objectAtIndex:currentSublayer++];
if ( [[currentLayer name] isEqualToString:#"FaceLayer"] ) {
featureLayer = currentLayer;
[currentLayer setHidden:NO];
}
}
// create a new one if necessary
if ( !featureLayer ) {
featureLayer = [CALayer new];
[featureLayer setContents:(id)[square CGImage]];
[featureLayer setName:#"FaceLayer"];
[previewLayer addSublayer:featureLayer];
[featureLayer release];
}
[featureLayer setFrame:faceRect];
switch (orientation) {
case UIDeviceOrientationPortrait:
[featureLayer setAffineTransform:CGAffineTransformMakeRotation(DegreesToRadians(0.))];
break;
case UIDeviceOrientationPortraitUpsideDown:
[featureLayer setAffineTransform:CGAffineTransformMakeRotation(DegreesToRadians(180.))];
break;
case UIDeviceOrientationLandscapeLeft:
[featureLayer setAffineTransform:CGAffineTransformMakeRotation(DegreesToRadians(90.))];
break;
case UIDeviceOrientationLandscapeRight:
[featureLayer setAffineTransform:CGAffineTransformMakeRotation(DegreesToRadians(-90.))];
break;
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationFaceDown:
default:
break; // leave the layer in its last known orientation
}
currentFeature++;
}
[CATransaction commit];
}

Automatically Sizing UIView after Adding to Window

Note: This may be a duplicate of Subview Doesnt AutoSize When Added to Root View Controller
I have an iPad app that switches between different views in its main window. The view-switching code looks like this:
- (void)switchToViewController:(UIViewController*)viewController {
if (currentViewController != viewController) {
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
[window addSubview:viewController.view];
}
}
The problem is that when the new view (a UISplitView) appears in landscape orientation, it is not sized to fill the entire window. There is a large empty black space on the right. It looks like the view is only 768 pixels wide, rather than the 1024-pixel width of the landscape window.
If I rotate the device to portrait and then back to landscape, the view sizes itself properly.
If the device is in portrait orientation, everything works fine. The UISplitView also gets sized properly if it is the first view I show. The problem only occurs if I switch to it after another view has been shown, in landscape.
So, is there some way to force iPhone OS to resize the view after it has been added to the window?
I've tried calling sizeToFit, and setNeedsLayout. I've also tried setting the view's bounds to the window's bounds, and I've tried setting the frame to match the previous view's frame.
This is absolutely possible! :-)
You can check out my repo here:
https://github.com/hfossli/AGWindowView
It will automatically deal with any rotation and framechanges so you won't have to worry about that.
If you like to worry about that then you can just cut and paste the most important parts
# 1 Add view to window
[[UIApplication sharedApplication] keyWindow] addSubview:aView];
# 2 Add listener and update view
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
Remember to remove notification listening
[[NSNotificationCenter defaultCenter] removeObserver:self];
# 3 Do the math
- (void)statusBarFrameOrOrientationChanged:(NSNotification *)notification
{
/*
This notification is most likely triggered inside an animation block,
therefore no animation is needed to perform this nice transition.
*/
[self rotateAccordingToStatusBarOrientationAndSupportedOrientations];
}
- (void)rotateAccordingToStatusBarOrientationAndSupportedOrientations
{
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
CGFloat angle = UIInterfaceOrientationAngleOfOrientation(statusBarOrientation);
CGFloat statusBarHeight = [[self class] getStatusBarHeight];
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
CGRect frame = [[self class] rectInWindowBounds:self.window.bounds statusBarOrientation:statusBarOrientation statusBarHeight:statusBarHeight];
[self setIfNotEqualTransform:transform frame:frame];
}
- (void)setIfNotEqualTransform:(CGAffineTransform)transform frame:(CGRect)frame
{
if(!CGAffineTransformEqualToTransform(self.transform, transform))
{
self.transform = transform;
}
if(!CGRectEqualToRect(self.frame, frame))
{
self.frame = frame;
}
}
+ (CGFloat)getStatusBarHeight
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(UIInterfaceOrientationIsLandscape(orientation))
{
return [UIApplication sharedApplication].statusBarFrame.size.width;
}
else
{
return [UIApplication sharedApplication].statusBarFrame.size.height;
}
}
+ (CGRect)rectInWindowBounds:(CGRect)windowBounds statusBarOrientation:(UIInterfaceOrientation)statusBarOrientation statusBarHeight:(CGFloat)statusBarHeight
{
CGRect frame = windowBounds;
frame.origin.x += statusBarOrientation == UIInterfaceOrientationLandscapeLeft ? statusBarHeight : 0;
frame.origin.y += statusBarOrientation == UIInterfaceOrientationPortrait ? statusBarHeight : 0;
frame.size.width -= UIInterfaceOrientationIsLandscape(statusBarOrientation) ? statusBarHeight : 0;
frame.size.height -= UIInterfaceOrientationIsPortrait(statusBarOrientation) ? statusBarHeight : 0;
return frame;
}
CGFloat UIInterfaceOrientationAngleOfOrientation(UIInterfaceOrientation orientation)
{
CGFloat angle;
switch (orientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = -M_PI_2;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI_2;
break;
default:
angle = 0.0;
break;
}
return angle;
}
UIInterfaceOrientationMask UIInterfaceOrientationMaskFromOrientation(UIInterfaceOrientation orientation)
{
return 1 << orientation;
}
Good luck!
This works, but it seems a little hacky:
- (void)switchToViewController:(UIViewController *)viewController {
if (viewController != currentViewController) {
UIInterfaceOrientation orientation = currentViewController.interfaceOrientation;
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
UIView *view = viewController.view;
// Set appropriate view frame (it won't be autosized by addSubview:)
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
if (UIInterfaceOrientationIsLandscape(orientation)) {
// Need to flip the X-Y coordinates for landscape
view.frame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width);
}
else {
view.frame = appFrame;
}
[window addSubview:view];
}
}
The window may include other UI elements besides your view. The 20 pixel difference in your example is the height of the status bar.
[[UIApplication sharedApplication] statusBarFrame].height;
Neither the window nor screen rotate. Getting their frames and using them for a rotated view will only work if you have switched the height and width.
If you are using a UIViewController, try returning YES from this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
I got the same problem, but i fixed it with this lines of code:
- (void)changeRow:(NSNotification *)notification {
[window addSubview:new.view];
[old.view removeFromSuperview];
[new.view removeFromSuperview];
[window addSubview:new.view];
}
You must add the new view, then remove the old and the new and then add the new view. I don't know why, but that works.
Fossli's answer is correct for iPad. However, I have a universal app that I needed to support. Therefore some adjustments are necessary.
Add the following to AppDelegate.h
#property (strong, nonatomic) UIImageView *imageView;
Add the following to AppDelegate.m
#synthesize imageView;
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (! (UIInterfaceOrientationIsLandscape(deviceOrientation) ||
UIInterfaceOrientationIsPortrait(deviceOrientation)))
{
// May be "UIInterfaceOrientationUnknown" which does not appear to be a defined value anywhere.
return;
}
[imageView setImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
/*
iOS Image Sizes
iPhone/iPod Portrait 320 x 480 (640 x 960 #2x)
iPad Portrait 768 x 1004 (1536 x 2008 #2x)
Landscape 1024 x 748 (2048 x 1496 #2x)
iPad window bounds in both orientations 768 x 1024 (needs manual swap in landscape)
iPhone window bounds in both orientations 320 x 480 (needs manual swap in landscape)
Note the size variations between the required default launch image sizes and
the size of the window bounds.
iPhone/iPod only requires rotations.
iPad needs origin or size adjustments depending on orientation.
*/
CGFloat angle = 0.0;
CGRect newFrame = [[self window] bounds];
// How to get size of status bar
// Size of status bar gets all wonky on rotations so just set it manually
// CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
CGSize statusBarSize = CGSizeMake(20.0, 20.0);
if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
angle = M_PI;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.height -= statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft)
{
angle = - M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.x += statusBarSize.height;
newFrame.size.width += statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeRight)
{
angle = M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.width -= statusBarSize.height;
}
}
else
{
angle = 0.0;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.y += statusBarSize.height;
newFrame.size.height -= statusBarSize.height;
}
}
imageView.transform = CGAffineTransformMakeRotation(angle);
imageView.frame = newFrame;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Add background image to window with orientation changes so that it is visible in all views.
// A listener is added since subviews do not receive orientation changes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object: nil];
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
[[self window] addSubview:imageView];
return YES;
}
Add the following to Utility.h
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation;
Add the following to Utility.m
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation
{
NSString *imageName = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~ipad.png";
}
else
{
imageName = #"Default-Portrait~ipad.png";
}
}
else
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~iphone.png";
}
else
{
imageName = #"Default.png";
}
}
return imageName;
}
Windows of iOS7 have different behaviors with windows of iOS8/9.
Keyboard window of iOS7 and all windows of iOS8/9 always have correct orientation & size. So you can observer the size change events and update the frame of your view.
But other windows of iOS7 always keep the portrait orientation and size. You need update transform of your view after rotation.
You need to observer UIApplicationWillChangeStatusBarOrientationNotification and update size of your UIView like this:
#interface MyView : UIView
#end
#implementation MyView
- (instancetype)init
{
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeOrientationHandler:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)updateTransformWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
CGFloat offset = (height - width) / 2;
CGAffineTransform transform;
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIInterfaceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(-M_PI);
break;
default:
transform = CGAffineTransformIdentity;
break;
}
self.transform = transform;
self.frame = CGRectMake(0, 0, width, height);
}
- (void)updateFrameWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
self.frame = CGRectMake(0, 0, height, width);
break;
default:
self.frame = CGRectMake(0, 0, width, height);
break;
}
}
- (void)updateWithOrientation:(UIInterfaceOrientation)orientation
{
BOOL isIos7 = [[UIDevice currentDevice].systemVersion floatValue] < 8.0;
BOOL isKeyboardWindow = [self.window isKindOfClass:NSClassFromString(#"UITextEffectsWindow")];
if (isIos7 == YES && isKeyboardWindow == NO) {
[self updateTransformWithOrientation:orientation];
} else {
[self updateFrameWithOrientation:orientation];
}
}
- (void)changeOrientationHandler:(NSNotification *)notification
{
[UIView animateWithDuration:0.25 animations:^{
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
[self updateWithOrientation:orientation];
}];
}
#end