Using CGAfflineTransformMakeScale/Rotation only does one action - ios

I am trying to make a video rotate and scale larger when the user rotates the screen to landscape.
- (void) orientationChanged:(NSNotification *)note
{
bool switchedLeft;
UIDevice * device = note.object;
switch(device.orientation)
{
case UIDeviceOrientationPortrait:
self.videoView.transform=CGAffineTransformMakeScale(0.5,0.5);
if (switchedLeft) {
self.videoView.transform=CGAffineTransformMakeRotation(-M_PI_2);
}else{
self.videoView.transform=CGAffineTransformMakeRotation(M_PI_2);
}
break;
case UIDeviceOrientationLandscapeLeft:
self.videoView.transform=CGAffineTransformMakeRotation(M_PI_2);
self.videoView.transform=CGAffineTransformMakeScale(2.0, 2.0);
switchedLeft=true;
break;
case UIDeviceOrientationLandscapeRight:
self.videoView.transform=CGAffineTransformMakeRotation(-M_PI_2);
self.videoView.transform=CGAffineTransformMakeScale(2.0, 2.0);
switchedLeft=false;
break;
default:
break;
};
}
There are a number of problems. First when I initially rotate to landscape it only does one transformation, in this configuration it just scales it.
The second problem is when I rotate to portrait it calls for the rotation but it never rotates. However i can go back and forth between landscape left and landscape right and it rotates properly. Any help would be greatly appreciated

You are essentially replacing the rotation transform with scale transform. In order to apply both, you need to use CGAffineTransformConcat().
CGAffineTransform rotate = CGAffineTransformMakeRotation(M_PI_2);
CGAffineTransform scale = CGAffineTransformMakeScale(2.0, 2.0);
self.videoView.transform = CGAffineTransformConcat(rotate, scale);
As for the second part, you don't need to apply another rotation, instead set it to default using CGAffineTransformIdentity.
case UIDeviceOrientationPortrait:
CGAffineTransform scale = CGAffineTransformMakeScale(0.5,0.5);
self.videoView.transform = CGAffineTransformConcat(CGAffineTransformIdentity, scale);
break;

try this
CGAffineTransform transform = CGAffineTransformRotate(self. videoView.transform, M_PI);
self. videoView.transform = transform;

Related

How do I convert device coordinates into UIView coordinates on iOS [duplicate]

My application uses CMMotionManager to track device motion, but iOS always returns device motion data in the standard device orientation (home button at the bottom).
To get the motion data into the same orientation as my UIView, I accumulate the view transforms from my view down to the window like this:
CGAffineTransform transform = self.view.transform;
for (UIView *superview = self.view.superview; superview; superview = superview.superview) {
CGAffineTransform superviewTransform = superview.transform;
transform = CGAffineTransformConcat(transform, superviewTransform);
}
This transform gets calculated correctly under iOS 6 & 7, but iOS 8 changes the rotation model and now the views always return the identity transform (no rotation) no matter how the device is oriented. The data from the motion manager is still fixed in the standard orientation though.
Monitoring UIDevice rotation notifications and manually calculating the four transforms seems like one approach to getting this transform under iOS 8, but it also seems bad since the device orientation won't necessarily match my view's orientation (ie, upside on iPhone is a device orientation not normally supported).
What's the best way to get the output from CMMotionManager into the orientation of a specific UIView under iOS 8?
Although not very obvious, the recommended way of doing this in iOS 8 and later is with transition coordinators.
In viewWillTransition(to:with:), the coordinator can pass you an object adopting UIViewControllerTransitionCoordinatorContext in the completion blocks of whichever of its methods you call (the default coordinator used by UIKit is actually its own context, but this isn't necessarily the case).
The context's targetTransform property is the rotation to be applied to the interface by the end of the animation. Note this is a relative transform, not the resulting absolute transform of the interface.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
let animation: (UIViewControllerTransitionCoordinatorContext) -> Void = { context in
// Update animatable properties.
}
coordinator.animate(alongsideTransition: animation) { context in
// Store or use the transform.
self.mostRecentTransform = context.targetTransform
}
}
While the old approach still works, this API is a bit more flexible when you need to coordinate animated transitions, such as when working with graphics frameworks or using a custom layout.
I couldn't find a way to directly calculate the transform, so instead I changed my code to calculate set the transform manually when a willRotateToInterfaceOrientation: message is received in my view controller, like this:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
CGAffineTransform transform;
switch (toInterfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft:
transform = CGAffineTransformMake(
0, -1,
1, 0,
0, 0);
break;
case UIInterfaceOrientationLandscapeRight:
transform = CGAffineTransformMake(
0, 1,
-1, 0,
0, 0);
break;
case UIInterfaceOrientationPortraitUpsideDown:
transform = CGAffineTransformMake(
-1, 0,
0, -1,
0, 0);
break;
case UIInterfaceOrientationPortrait:
transform = CGAffineTransformIdentity;
break;
}
self.motionTransform = transform;
}

UIToolbar Rotation/Animation

I have a UIToolbar for an iPad app that is meant to stay at the top of the screen for all orientations. The UIView that my UIToolbar is part of does not rotate, however. The UIToolbar DOES rotate, but I'm having trouble getting the rotations to work the way I want...
I can't figure out how to change the size of the toolbar without stretching the contents of the toolbar (setting a scale transformation) or without messing up the transformations (setting the frame or the bounds).
- (void)updateOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated
{
CGAffineTransform toolbarTransform;
switch(orientation) {
case UIDeviceOrientationPortrait:
toolbarTransform = CGAffineTransformIdentity;
break;
case UIDeviceOrientationLandscapeRight:
toolbarTransform = CGAffineTransformMakeTranslation(-768 / 2 + 44 / 2, 768 / 2 - 44 / 2 + 1024 - 768);
toolbarTransform = CGAffineTransformRotate(toolbarTransform, degreesToRadian(-90));
break;
case UIDeviceOrientationLandscapeLeft:
toolbarTransform = CGAffineTransformMakeTranslation(768 / 2 - 44 / 2, 768 / 2 - 44 / 2);
toolbarTransform = CGAffineTransformRotate(toolbarTransform, degreesToRadian(90));
break;
case UIDeviceOrientationPortraitUpsideDown:
toolbarTransform = CGAffineTransformMakeTranslation(0, 1024 - 44);
toolbarTransform = CGAffineTransformRotate(toolbarTransform, degreesToRadian(180));
break;
default:
toolbarTransform = CGAffineTransformIdentity;
break;
}
if(animated)
{
[UIView animateWithDuration:.5 animations:^
{
self.toolbar.transform = toolbarTransform;
}];
}
else
{
self.toolbar.transform = toolbarTransform;
}
}
This code mostly positions the toolbar how I want, but what should I do to resize the toolbar without messing up the transformations or stretching the toolbar's contents? Or maybe I'm approaching this wrong altogether?
edit: Slightly updated my code. Positioning is correct, just need to resize them somehow...
The solution to this issue was to create a special UIView (called rotationView) and performing all rotation/translations to that UIView instead of directly to the UIToolbar. Then, I can change the toolbar.frame safely without "messing up" the transformations.

Augmented Reality App in Landscape Mode

I am trying to build an augmented reality app using the CoreMotion framework. I have tried to go off of Apple's pARk sample code project, but it only works in portrait mode. I need it to work in landscape. When switched to landscape mode the subviews in the overlay view move in the opposite directions and at the wrong rate (they either move too fast or too slow across screen)
I have read other postings that provide two solutions:
Create a reference attitude and apply the inverse of that attitude to the current attitude, as suggested in the CoreMotion Tea Pot Example.
Rotate the quaternion representation of the attitude 90 degrees
I do not think that the first will work because my augmented reality app requires that it be referenced to true north.
I also do not understand the math required to do the second.
Any suggestions on how to accomplish this complex problem I welcome.
How far are you now in your augmented reality app? I think beginning by taking a look at PARk from Apple is harsh. You need to have some advanced mathematical understanding. But if you do, why not!
you can take a look at this repository, this an augmented reality project working on Portrait and Landscape mode. Here is how the rotation is handled:
- (void)deviceOrientationDidChange:(NSNotification *)notification {
prevHeading = HEADING_NOT_SET;
[self currentDeviceOrientation];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
// Later we may handle the Orientation of Faceup to show a Map. For now let's ignore it.
if (orientation != UIDeviceOrientationUnknown && orientation != UIDeviceOrientationFaceUp && orientation != UIDeviceOrientationFaceDown) {
CGAffineTransform transform = CGAffineTransformMakeRotation(degreesToRadian(0));
CGRect bounds = [[UIScreen mainScreen] bounds];
switch (orientation) {
case UIDeviceOrientationLandscapeLeft:
transform = CGAffineTransformMakeRotation(degreesToRadian(90));
bounds.size.width = [[UIScreen mainScreen] bounds].size.height;
bounds.size.height = [[UIScreen mainScreen] bounds].size.width;
break;
case UIDeviceOrientationLandscapeRight:
transform = CGAffineTransformMakeRotation(degreesToRadian(-90));
bounds.size.width = [[UIScreen mainScreen] bounds].size.height;
bounds.size.height = [[UIScreen mainScreen] bounds].size.width;
break;
case UIDeviceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(degreesToRadian(180));
break;
default:
break;
}
[displayView setTransform:CGAffineTransformIdentity];
[displayView setTransform: transform];
[displayView setBounds:bounds];
degreeRange = [self displayView].bounds.size.width / ADJUST_BY;
}
}
You have to rotate all your annotations and then your overlay view after the device rotation!
If you're really stuck, and are willing to use another toolkit, try iPhone-AR-Toolkit. It works in both portrait and landscape.

CGAffineTransform scaling and rotating

I have an imageview that I am scaling and rotating via UISliders. Here is how I do so:
- (IBAction)sizeSlider:(UISlider *)sender
{
int SCALE_MAX = 200;
int SCALE_MIN = 10;
CGAffineTransform transform = image.transform;
float scale = sqrt(transform.a*transform.a + transform.c*transform.c);
if (scale > SCALE_MAX)
image.transform = CGAffineTransformScale(transform, SCALE_MAX/scale*sender.value, SCALE_MAX/scale*sender.value);
else if (scale < SCALE_MIN)
image.transform = CGAffineTransformScale(transform, SCALE_MIN/scale*sender.value, SCALE_MIN/scale*sender.value);
}
- (IBAction)angleSlider:(UISlider *)sender
{
CGAffineTransform t = CGAffineTransformMakeRotation(sender.value);
image.transform = t;
}
This works, however if the imageview has been scaled from its original size then rotating the image resets it to its original size. How can I prevent this?
Use the similar approach of your scale in your rotation. Meaning, use:
CGAffineTransformRotate(image.transform, theAngle);
CGAffineTransformMakeRotation
creates a new transform based on identity. Scaling is part of a transform--you want to use
CGAffineTransformRotate
instead which adds rotation to the existing, scaled transform.

How to track successive CGAffineTransforms so as to arrive at a specific point on screen?

How do you execute multiple CGAffineTransform operations (in animation blocks) without keeping track of every operation executed?
The translation operation doesn't take x,y coordinates but instead values to shift by. So unless you know where you are currently translated to, say at "location 2," how do you know what values to shift by to get to "location 3?"
For example:
A UIView's frame is at (0, 0) - Position 1. I set the transform to translate to (768, 0) and rotate -90 degrees - Position 2. Some time passes and now I want to move to (768, 1024) and rotate another -90 degrees - Position 3.
How do I know what to translate by to move from Position 2 to Position 3?
In context, I'm trying to achieve an iPad view like the following:
a UIView that takes up the entire screen
a UIToolbar that takes up the top edge and is on top of the UIView
when the iPad rotates, the UIView stays with the device, but the toolbar will rotate so that it is always on the top edge of the screen.
I am using CGAffineTransform translate and rotate to move the toolbar. Works great, except when I rotate the iPad multiple times. The first translate/rotate will work perfect. The following transforms will be off because I don't know the correct values to shift by.
UPDATE:
It looks like if I take the current translation (tx, ty) values in the UIView.transform struct and use the difference between them and the new location, it works. However, if the view has been rotated, this does not work. The tx and ty values can be flipped because of the previous rotation. I'm not sure how to handle that.
UPDATE 2:
Doing some research, I've found that I can get the original, unrotated points from tx, ty by getting the abs value of the points and possibly swapping x and y if the UIView is perpendicular. Now I am stuck figuring out how to correctly apply the next set of transforms in the right order. It seems no matter how I concat them, the UIView ends up in the wrong place. It just seems like this is all too complicated and there must be an easier way.
The answer is, apparently, you don't track the transforms.
So the way to rotate the toolbar around the screen is by not concatenating a rotate and translate transform. Instead, create a rotate transform and set the frame in the animation block. Further, based on the new UIInterfaceOrientation, set the degrees to rotate based on the compass values of 0, -90, -180, -270. Also, set the frame size base on the same locations.
So:
CGPoint portrait = CGPointMake(0, 0);
CGPoint landscapeLeft = CGPointMake(768 - 44, 0);
CGPoint landscapeRight = CGPointMake(0, 0);
CGPoint upsideDown = CGPointMake(0, 1024 - 44);
CGSize portraitSize = CGSizeMake(768, 44);
CGSize landscapeLeftSize = CGSizeMake(44, 1024);
CGSize landscapeRightSize = CGSizeMake(44, 1024);
CGSize upsideDownSize = CGSizeMake(768, 44);
CGFloat rotation;
CGRect newLocation;
switch(orientation) {
case UIDeviceOrientationPortrait:
NSLog(#"Changing to Portrait");
newLocation.origin = portrait;
newLocation.size = portraitSize;
rotation = 0.0;
break;
case UIDeviceOrientationLandscapeRight:
NSLog(#"Changing to Landscape Right");
newLocation.origin = landscapeRight;
newLocation.size = landscapeRightSize;
rotation = -90.0;
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(#"Changing to Landscape Left");
newLocation.origin = landscapeLeft;
newLocation.size = landscapeLeftSize;
rotation = -270.0;
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(#"Changing to Upside Down");
newLocation.origin = upsideDown;
newLocation.size = upsideDownSize;
rotation = -180.0;
break;
default:
NSLog(#"Unknown orientation: %d", orientation);
newLocation.origin = portrait;
newLocation.size = portraitSize;
rotation = 0.0;
break;
}
CGRect frame = newLocation;
CGAffineTransform transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(rotation));
if(lastOrientation) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationBeginsFromCurrentState:YES];
}
toolbar.transform = transform;
toolbar.frame = frame;
// Commit the changes
if(lastOrientation) {
[UIView commitAnimations];
}
lastOrientation = orientation;
This works beautifully. However, an unexpected problem is that UI elements that iOS shows on your behalf are not oriented correctly. I.e., modal windows and popovers all keep the same orientation as the underlying UIView. That problem renders this whole thing moot.

Resources