Xcode 6 iOS 8 Rotation Issue - Large Blank Space On Screen - ios

I am running into a very strange issue with my iPad app in Xcode 6. Previously (When building with Xcode 5 / iOS 7 SDK) it would handle rotations with no problem, but now that developers are required to build with Xcode 6 / iOS 8 SDK, my app no longer handles rotation properly.
I did some research and was able to use the viewWillTransitionToSize method in order to get all my subviews to properly rotate and resize themselves. However, my screen has a large white rectangle along the side when I rotate. If I start the app in portrait and rotate to landscape, it goes from looking normal to having a large white space on the right.
(The same thing happens if I start in landscape and rotate to portrait, but the white space is on the bottom).
My subviews are definitely resizing themselves properly, but that white space unfortunately covers them up. I checked the both the bounds and frame of my UIScreen window and those values seem valid (1024w x 768h in landscape, 768w x 1024h in portrait). Previously it was a black space but once I added the line [self.window setFrame:[[UIScreen mainScreen] bounds]]; to my didFinishLaunchingWithOptions method, it became a white space.
The following is the viewWillTransitionToSize code that I'm using:
- (void) viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size WithTransitionCoordinator:coordinator];
UIInterfaceOrientation *orientation = [self interfaceFromTransform:[coordinator targetTransform]];
UIInterfaceOrientation *oldOrientation = [[UIApplication sharedApplication] statusBarOrientation];
[self willRotateToInterfaceOrientation:orientation duration:1.0];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> content)
{
[self willAnimateRotationToInterfaceOrientation:orientation duration:1.0];
}
completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
[self didRotateFromInterfaceOrientation:oldOrientation];
}];
CGFloat height = self.view.frame.size.height;
CGFloat width = self.view.frame.size.width;
[self.view setFrame:CGRectMake(0, 0, height, width)];
}
interfaceFromTransform is a method I created to determine which interface the transform is rotating to:
- (UIInterfaceOrientation) interfaceFromTransform: (CGAffineTransform)transform
{
UIInterfaceOrientation old = [[UIApplication sharedApplication] statusBarOrientation];
int rotation = 0;
if (transform.b == -1 && transform.c == 1)
rotation = 90;
if (transform.b == 1 && transform.c == -1)
rotation = -90;
if (transform.a == -1 && transform.d == -1)
rotation = 180;
if (old == UIInterfaceOrientationLandscapeRight)
{
if (rotation == 90)
return UIInterfaceOrientationPortrait;
if (rotation == -90)
return UIInterfaceOrientationPortraitUpsideDown;
if (rotation == 180)
return UIInterfaceOrientationLandscapeLeft;
}
if (old == UIInterfaceOrientationLandscapeLeft)
{
if (rotation == 90)
return UIInterfaceOrientationPortraitUpsideDown;
if (rotation == -90)
return UIInterfaceOrientationPortrait;
if (rotation == 180)
return UIInterfaceOrientationLandscapeRight;
}
if (old == UIInterfaceOrientationPortraitUpsideDown)
{
if (rotation == 90)
return UIInterfaceOrientationLandscapeRight;
if (rotation == -90)
return UIInterfaceOrientationLandscapeLeft;
if (rotation == 180)
return UIInterfaceOrientationPortrait;
}
if (old == UIInterfaceOrientationPortrait)
{
if (rotation == 90)
return UIInterfaceOrientationLandscapeLeft;
if (rotation == -90)
return UIInterfaceOrientationLandscapeRight;
if (rotation == 180)
return UIInterfaceOrientationPortraitUpsideDown;
}
return old;
}
Yet despite all this, that white space still refuses to go away. Is there something obvious that I'm missing that will keep it from appearing when I rotate the screen?

As it turns out, I had disabled "Autoresize Subviews" in my main window nib file. Enabling this immediately fixed the problem.

Related

Converting CoreMotion Portrait Angles to Landscape Right and Landscape Left in Objective-C iOS

I am trying to get CoreMotion to behave correctly when the user's device is in Landscape Mode. The below code is working for Portrait mode but I can't figure out how to convert it to Landscape Left and Landscape Right. If you are familiar with Quaternions and CoreMotion, any help would be hugely appreciated.
The Problem:
There is a ScrollView which contains an image view, this image view can only be partially seen at any given point, and is revealed when the user rotates their phone to aim at it. Imagine sitting in the front row of a movie theater and turning your head in all directions to see the entire screen.
I am using a quaterion for the yaw variable because the true 'yaw' from the attitude seems to be for if the phone was being held flat.
When turning the phone in either landscape mode, I need to know the quaterion equations to calculate the correct degrees for the image and the device.
// set content offset for image inside scrollview
CGFloat offsetX;
if (self.deviceDegreesZ >= 180) {
offsetX = (_maximumXOffset/2) + (_maximumXOffset/2) * (fabs(self.deviceDegreesZ - 180))/90;
} else {
offsetX = (_maximumXOffset/2) - (_maximumXOffset/2) * (fabs(self.deviceDegreesZ - 180))/90;
}
CGFloat offsetY;
if (self.deviceDegreesY >= 90) {
offsetY = (_maximumYOffset/2) + (_maximumYOffset/2) * (fabs(self.deviceDegreesY - 90))/45;
} else {
offsetY = (_maximumYOffset/2) - (_maximumYOffset/2) * (fabs(self.deviceDegreesY - 90))/45;
}
if (offsetX != 0 && offsetY != 0) {
[_scrollView setContentOffset:CGPointMake(offsetX, offsetY) animated:NO];
}
// device angles used to set content offset above and look at imageview
// Quaternion
CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
int thePitch = self.motionManager.deviceMotion.attitude.pitch * (180.0 / M_PI);
int theRoll = self.motionManager.deviceMotion.attitude.roll * (180.0 / M_PI);
int theYaw = radiansToDegrees(asin(2*(quat.x*quat.z - quat.w*quat.y)));
// gravity
CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
double rotation = atan2(gravity.x, gravity.y) - M_PI;
self.imageView.transform = CGAffineTransformMakeRotation(rotation);
// portrait (working)
if ([[UIDevice currentDevice]orientation] == UIInterfaceOrientationPortrait){
// X
if (quat.y >= 0.5f) {
self.deviceDegreesZ = radiansToDegrees(asin(2*quat.x*quat.y + 2*quat.w*quat.z));
} else if (quat.y <= -0.5f) {
self.deviceDegreesZ = 360 + radiansToDegrees(asin(2*quat.x*quat.y + 2*quat.w*quat.z));
} else {
self.deviceDegreesZ = 180 - radiansToDegrees(asin(2*quat.x*quat.y + 2*quat.w*quat.z));
}
// Y
CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
if (gravity.z >= 0) {
self.deviceDegreesY = thePitch; // - abs(theYaw)
} else {
self.deviceDegreesY = 180.f - thePitch; // - abs(theYaw)
}
}
// landscape left
if ([[UIDevice currentDevice]orientation] == UIInterfaceOrientationLandscapeLeft){
// ???
}
// landscape right
if ([[UIDevice currentDevice]orientation] == UIInterfaceOrientationLandscapeRight){
// ???
}

GPUImageMovie not respecting imageOrientation

I am using GPUImageMovie which is playing a movie file, this file was recorded on a iOS device.
However when GPUImageMovie plays it it is in the wrong orientation so the video isn't rotated to be shown correctly.
How do I get it to respect it's orientation ? I've tried modifying the OpenGL code with no luck.
I had same problem when playing a video recorded in iOS device using GPUImageMovie. I solved it using following functions :
Call this setRotationForFilter: method by passing you filter. The orientationForTrack: will return your current video orientation.
- (void)setRotationForFilter:(GPUImageOutput<GPUImageInput> *)filterRef {
UIInterfaceOrientation orientation = [self orientationForTrack:self.playerItem.asset];
if (orientation == UIInterfaceOrientationPortrait) {
[filterRef setInputRotation:kGPUImageRotateRight atIndex:0];
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
[filterRef setInputRotation:kGPUImageRotate180 atIndex:0];
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
[filterRef setInputRotation:kGPUImageRotateLeft atIndex:0];
}
}
- (UIInterfaceOrientation)orientationForTrack:(AVAsset *)asset
{
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
CGSize size = [videoTrack naturalSize];
CGAffineTransform txf = [videoTrack preferredTransform];
if (size.width == txf.tx && size.height == txf.ty)
return UIInterfaceOrientationLandscapeRight;
else if (txf.tx == 0 && txf.ty == 0)
return UIInterfaceOrientationLandscapeLeft;
else if (txf.tx == 0 && txf.ty == size.width)
return UIInterfaceOrientationPortraitUpsideDown;
else
return UIInterfaceOrientationPortrait;
}
Hope this solves your issue.
add to #Ameet Dhas:
one place to set rotation is where before call next filter.
[currentTarget setInputRotation:outPutRotation atIndex:targetTextureIndex];
[currentTarget newFrameReadyAtTime:currentSampleTime atIndex:targetTextureIndex];

change screen rotation of app extension in objective c

How do I lock from rotating screen, I want my app extension to be only in portrait mode.
When I'm using my extension inside Photos my I can rotate the screen to landscape.
thanks
You have to choice:
1- Set landscape and portrait as supported interface orientation in the project, and then for each ViewController, you will override the supported interface orientation;
2- Set only portrait mode in the project, and in the ViewController you need it, you can make a 90 degree rotation
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
EDIT: so you can do this. Set your controller as observer for the keyPath UIDeviceOrientationDidChangeNotification. Then:
-(void)changedOrientation: (NSNotification *)note
{
if (note)
{
UIDevice *dev = (UIDevice *)note.object;
if ([dev orientation] == UIInterfaceOrientationLandscapeRight)
{
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
}
else if ([dev orientation] == UIInterfaceOrientationPortrait)
{
self.view.transform = CGAffineTransformIdentity;
}
else if ([dev orientation] == UIInterfaceOrientationPortraitUpsideDown)
{
self.view.transform = CGAffineTransformIdentity;
}
else if ([dev orientation] == UIInterfaceOrientationLandscapeLeft)
{
self.view.transform = CGAffineTransformMakeRotation(-M_PI_2);
}
}
}

UIScreenEdgePanGestureRecognizer failing to recognize gesture on right edge

I am having an issue where i have defined a UIScreenEdgePanGestureRecognizer to detect pan gesture appearing on the right edge of my device but the gesture is recognized sporadically:
I have the following code:
_swipeInLeftGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeInFromRightEdge:)];
_swipeInLeftGestureRecognizer.minimumNumberOfTouches = 1;
_swipeInLeftGestureRecognizer.maximumNumberOfTouches = 1;
[_swipeInLeftGestureRecognizer setEdges:UIRectEdgeRight];
[self.view addGestureRecognizer:_swipeInLeftGestureRecognizer];
- (void)handleSwipeInFromRightEdge:(UIGestureRecognizer*)sender
{
NSLog(#"swipe from right edge!!!!");
}
The gesture is attached to a view with nothing on it.
Am I missing something?
I've managed to create a workaround. It is pretty straightforward. I've subclassed UIWindow and used touchesBegan/touchesMoved/etc. methods to simulate gesture recognition.
It works. UIWindow is not rotated automatically, so I have to transform touch coordinates accordingly.
Here is my version of the transformation:
- (CGPoint)transformPoint:(CGPoint)point {
CGPoint pointInView = point;
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) {
pointInView.x = self.bounds.size.width - pointInView.x;
pointInView.y = self.bounds.size.height - pointInView.y;
} else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
CGFloat x = pointInView.x;
CGFloat y = pointInView.y;
pointInView = CGPointMake(self.bounds.size.height - y, x);
} else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight) {
CGFloat x = pointInView.x;
CGFloat y = pointInView.y;
pointInView = CGPointMake(y, self.bounds.size.width - x);
}
return pointInView;
}
I think there's bugs. My app is always in landscape mode, and I've set up the same thing as you, with it detecting the right edge. If that edge is the camera side of the iPad, it detects sporadically. If I flip over the iPad and the right edge is the button side, it works just fine. In fact, I'm having this problem with any gesture, not just this one.

How can I re adjust my images to be proportional and fit on orientation change?

When an image appears in my modal view, I want the image to scale correctly, like so:
If the view is landscape:
If it is a landscape image it should fit to the screen bounds
If it is a portrait image it should center
If the view is portrait
If it is a portrait image fit vertical center
Same for landscape image
I have the portrait view ok because I use scale to aspect fit, which works great in portrait
However, no matter what I do I cannot seem to get the images to scale/resize/position correctly when I change to landscape.
Thank you in advance.
(void)viewDidLoad
{
[super viewDidLoad];
self.scollView.delegate=self;
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.viewImage addGestureRecognizer:doubleTap];
}
- (void)resetContentMode:(UIImageView *)imageView
{
if (imageView.image == nil)
return;
CGSize imageSize = imageView.image.size;
CGSize imageViewSize = imageView.bounds.size;
if (imageSize.height == 0 || imageViewSize.height == 0)
return;
CGFloat imageRatio = imageSize.width / imageSize.height;
CGFloat imageViewRatio = imageViewSize.width / imageViewSize.height;
CGFloat percentDifference = fabs(imageRatio - imageViewRatio) / imageViewRatio;
if (percentDifference < 0.25)
imageView.contentMode = UIViewContentModeScaleAspectFill;
else
imageView.contentMode = UIViewContentModeScaleAspectFit;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
-(void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if(toInterfaceOrientation == UIInterfaceOrientationPortrait){
[self resetContentMode:im];
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight || toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
[self resetContentMode:im];
}
}
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
[self.scollView setZoomScale:self.scollView.minimumZoomScale animated:YES];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
[self resetContentMode:im];
}
else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
[self resetContentMode:im];
}
}
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];}
If you don't need pinch-zoom, but rather just need to tweak the appearance of the image view based upon whether the orientation of the image matches that of the image view, you can retire the scrollview in this process.
You can accomplish the desired effect with just an image view, but changing the contentMode based upon how close the ratio of the image dimensions are to the image view's ratio of dimensions. If they're close, I'll default to UIViewContentModeScaleAspectFill (and because the ratios are close to each other, I don't have to worry about too much getting clipped). If they're not close, I'll default to UIViewContentModeScaleAspectFit (because the ratios are not close, I know a lot would get clipped if I used "fill", so instead I default to "fit").
- (void)resetContentMode:(UIImageView *)imageView
{
if (imageView.image == nil)
return;
CGSize imageSize = imageView.image.size;
CGSize imageViewSize = imageView.bounds.size;
if (imageSize.height == 0 || imageViewSize.height == 0)
return;
CGFloat imageRatio = imageSize.width / imageSize.height;
CGFloat imageViewRatio = imageViewSize.width / imageViewSize.height;
CGFloat percentDifference = fabs(imageRatio - imageViewRatio) / imageViewRatio;
if (percentDifference < 0.25)
imageView.contentMode = UIViewContentModeScaleAspectFill;
else
imageView.contentMode = UIViewContentModeScaleAspectFit;
}
I then, whenever I rotate, call this method again. For example:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self resetContentMode:self.imageView];
}
I also turn on userInteractionEnabled on the image, and have a double tap gesture that toggles between the two content modes:
- (void)handleDoubleTap:(UITapGestureRecognizer *)gesture
{
UIImageView *imageView = (id)gesture.view;
if (imageView.contentMode == UIViewContentModeScaleAspectFit)
imageView.contentMode = UIViewContentModeScaleAspectFill;
else if (imageView.contentMode == UIViewContentModeScaleAspectFill)
imageView.contentMode = UIViewContentModeScaleAspectFit;
else
[self resetContentMode:imageView];
}
I'll also make sure that the UIImageView has its clipsToBounds set to YES, such that when it's using UIViewContentModeScaleAspectFill, I don't have to worry about the image bleeding over the bounds of the image view.
This is a quick-and-dirty solution, but it works pretty well for simple scenarios, where you only want to worry about aspect fit vs aspect fill.

Resources