Determine if UIImageView has been rotated - ios

I am rotating a UIImageView in this way:
-(IBAction)rotateImageView:(id)sender{
photoView.transform = CGAffineTransformRotate(photoView.transform, M_PI);
}
As you can see the image can either be up or down. Is there the possibility to detect if the image is up or down, in order to do something like this:
if(image is up)....

If you just want to tell if the image has been rotated using an affine transform, as your question implies, you can do this:
if (CGAffineTransformIsIdentity(photoView.transform)) {
// not rotated
...
} else {
// rotated
...
}
If you want to check for a particular rotation, do this:
CGAffineTransform t = CGAffineTransformMakeRotation(M_PI);
if (CGAffineTransformEqualToTransform(photoView.transform, t)) {
// rotated by M_PI
...
}
Note that the above two solutions only work if you are not applying OTHER affine transforms to the view at the same time as the rotation transforms. If you are also applying other transforms, perhaps you are better off just tracking the rotation state in a variable.

Swift 4
if transform.isIdentity { }

You can check the angle of the photoView, if it is 0.00 that means its up and if its 3 or 3.13 or any close value to it then it shows that its down. (you can log the exact value of down).
CGFloat angle = [(NSNumber *)[photoView valueForKeyPath:#"layer.transform.rotation.z"] floatValue];
NSLog(#"%f", angle);

Related

Rotating a view in iOS

I have a UIView in my view. Right now I have a button moving the UIView on it's y axis. Here is my code
CGRect frame2 = self.test22.frame;
frame2.origin.y -= 1.f/[UIScreen mainScreen].scale; //however many pixels to the right..
self.test22.frame = frame2;
But how can I rotate it? Except using the x or y axis. I want to be able to move the UIView by rotation by 1.f.
To Rotate the UIView create the object of UIView (*view) and make transform on it
Lets to rotate the view 90 degree use below code
CGAffineTransform r_transform = CGAffineTransformIdentity;
r_transform = CGAffineTransformRotate(r_transform,DegreeYoRadians(90));
view.transform = r_transform;
Welcome to SO. You want to look at the UIView method animateWithDuration, and the view's transform property. You'd apply a rotation transform to the view in your animation block. Note that if you change a view's transform, you should not read or write the frame property. Instead use the center property to change the position and the bounds.size property if you need to change the size.
Your code might look like this:
[UIView animateWithDuration: .25
animations: ^
{
self.test22.transform = CGAffineTransformMakeRotation(M_PI/2);
CGPoint center = self.test22.center;
center.y -= 1;
self.test22.center = center;
}
];
EDIT:
I have no idea what you mean when you say "I want to be able to move the UIView by rotation by 1.f." Rotating a view is not moving it, it's rotating it. And rotation is expressed as an angle, not a value like "1". Furthermore, the angle is in radians, where π is 180 degrees, π/2 is 90 degrees, and 2π is a full-circle rotation (or no change, since it puts the rotation back at it's original value.) You have to think in terms of fractions of 2π when you do rotations. Or you can use degrees and convert to radians, where:
degrees = radians * 180/π
Also, your code that moves the view will only change the view's position by half a point on retina displays which doesn't make much sense.

Make a shape's position fixed on iOS camera preview

I am working on an iOS camera based app, in which I have to capture a first point and then I need to draw the line to the current focus point to the first captured point. MagicPlan works this way.
Here is an image:
I have tried to fix a point for first point using accelerometer values and the tilted angle of the device. But, no luck so far. And how would i draw the line to the second point from the first point?
This is the code that i have tried so far:
if (self.motionManager.deviceMotionAvailable)
{
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error) {
CATransform3D transform;
transform = CATransform3DMakeRotation(motion.attitude.pitch, 1, 0, 0);
transform = CATransform3DRotate(transform,motion.attitude.roll, 0, 1, 0);
transform = CATransform3DRotate(transform,motion.attitude.yaw, 0, 0, 1);
self.viewObject.layer.transform = transform;
}];
}
if (self.motionManager.deviceMotionActive)
{
/**
* Pulling gravity values from deviceMotion sensor
*/
CGFloat x = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.x];
CGFloat y = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.y];
CGFloat z = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.z];
CGFloat r = sqrtf(x*x + y*y + z*z);
/**
* Calculating device forward/backward title angle in degrees
*/
CGFloat tiltForwardBackward = acosf(z/r) * 180.0f / M_PI - 90.0f;
[self.lblTilForwardBackward setText:[#(tiltForwardBackward) stringValue]];
}
You have a lot of issues to resolve here. It isn't just a matter of adjusting for camera orientation as the height that the camera is being held at and position of the camera in the room are also changing. Even in MagicPlan, when the person turns around, the camera moves (rotates about the axis going through the person's head down to his feet).
There is quite a lot of algebra and rotation/translation matrix operations to work out. No one is going to do this for you. You'll have to figure it all out and derive it yourself (or look it up from old graphics text books).
I suggest doing something as straight forward and multi-step as possible (so you can debug each step along the way). Assume flat ground (indoor environment).
Get camera position/orientation/focal length from the first snapshot.
Figure out the touch point in real world Cartesian coordinates(start with video coordinates and translate via roll/pitch/yaw and ray traced projection to ground plane(using camera height).
From the focal length you can figure out the field of view and depth to center of field of view and using camera orientation and click distance from center of screen determine xyz offset from some origin (your feet maybe).
Determine and track camera position and orientation relative to that origin.
On second snapshot (or motion awake), figure out (center or touched point) distance from origin and exact xyz (as above).
Once you have those two points in xyz you can plot the line by taking the standard orthogonal projection onto the view plane. Clipping as needed in case original point is out of the FOV.

check if imageview has been rotated — Objective-c

I have an imageview that are being rotated PI/4 (radians) every time it's taped.
That works fine with this code:
- (void)handleTap:(UITapGestureRecognizer *)tapRecognize
{
if (tapRecognize == tapRecognizer)
{
CGAffineTransform transform = CGAffineTransformRotate(imageview.transform, (M_PI / 4));
[imageview setTransform:transform];
}
The tapRecognizer is asigned to the imageview.
Now, I want to check if the imageview has been rotated. This is my code:
if (CGAffineTransformEqualToTransform(imageview.transform, rot45)) //rot45 is a CGAffineTransformMakeRotation variable which is set to M_PI / 4
{
NSLog("Rotated");
}
That works fine for the first tap, when it has been rotated 45°. But I want to be able to check when it has been taped two times, which means that it has been rotated 90°. And so on. I want different actions on each rotation-angle. How can I check that?
Sorry if the question is unclear
Devise a scheme to map the tag property to rotation. On every rotation update the tag value.

IOS: rotate an uiimageview

in my app I have an imageview, its name is arrow and I rotate it of 180 degrees in this way:
arrow.transform = CGAffineTransformMakeRotation(M_PI);
it work fine, but now I want that this imageview return in the original position; what's the value taht I should set to obtain it?
To make it return to it's original position, just do this:
arrow.transform = CGAffineTransformMakeRotation(0);
In degrees, 0 radians is 0 degrees, so it should return to its original position.
Try setting the transform property back to the identity matrix. i.e. CGAffineTransformIdentity
Swift3:
arrow.transform = CGAffineTransform(rotationAngle: rotation);
where rotation is the angle to rotate in radians.

iOS CAKeyframeAnimation rotationMode on UIImageView

I am experimenting with Key Frame animation of the position of a UIImageView object moving along a bezier path. This pic shows the initial state before animation. The blue line is the path - initially moving straight up, the light green box is the initial bounding box or the image, and the dark green "ghost" is the image that I am moving:
When I kick off the animation with rotationMode set to nil, the image keeps the same orientation all the way through the path as expected.
But when I kick off the animation with rotationMode set to kCAAnimationRotateAuto, the image immediately rotates 90 degrees anti-clockwise and keeps this orientation all the way through the path. When it reaches the end of the path it redraws in the correct orientation (well it actually shows the UIImageView that I repositioned in the final location)
I was naively expecting that the rotationMode would orientate the image to the tangent of the path and not to the normal, especially when the Apple docs for the CAKeyframeAnimation rotationMode state
Determines whether objects animating along the path rotate to match the path tangent.
So what is the solution here? Do I have to pre-rotate the image by 90 degrees clockwise? Or is there something that I am missing?
Thanks for your help.
Edit 2nd March
I added a rotation step before the path animation using an Affine rotation like:
theImage.transform = CGAffineTransformRotate(theImage.transform,90.0*M_PI/180);
and then after the path animation, resetting the rotation with:
theImage.transform = CGAffineTransformIdentity;
This makes the image follow the path in the expected manner. However I am now running into a different problem of the image flickering. I am already looking for a solution to the flickering issue in this SO question:
iOS CAKeyFrameAnimation scaling flickers at animation end
So now I don't know if I have made things better or worse!
Edit March 12
While Caleb pointed out that yes, I did have to pre rotate my image, Rob provided an awesome
package of code that almost completely solved my problems. The only thing that Rob didn't do was compensating for my assets being drawn with a vertical rather than horizontal orientation, thus still requiring to preRotate them by 90 degrees before doing the animation. But hey, its only fair that I have to do some of the work to get things running.
So my slight changes to Rob's solution to suite my requirements are:
When I add the UIView, I pre Rotate it to counter the inherent rotation added by setting the rotationMode:
theImage.transform = CGAffineTransformMakeRotation(90*M_PI/180.0);
I need to keep that rotation at the end of the animation, so instead of just blasting the view's transform with a new scale factor after the completion block is defined, I build the scale based on the current transform:
theImage.transform = CGAffineTransformScale(theImage.transform, scaleFactor, scaleFactor);
And that's all I had to do to get my image to follow the path as I expected!
Edit March 22
I have just uploaded to GitHub a demo project that shows off the moving of an object along a bezier path. The code can be found at PathMove
I also wrote about it in my blog at Moving objects along a bezier path in iOS
The issue here is that Core Animation's autorotation keeps the horizontal axis of the view parallel to the path's tangent. That's just how it works.
If you want your view's vertical axis to follow the path's tangent instead, rotating the contents of the view as you're currently doing is the reasonable thing to do.
Here's what you need to know to eliminate the flicker:
As Caleb sort of pointed out, Core Animation rotates your layer so that its positive X axis lies along the tangent of your path. You need to make your image's “natural” orientation work with that. So, supposing that's a green spaceship in your example images, you need the spaceship to point to the right when it doesn't have rotation applied to it.
Setting a transform that includes rotation interferes with the rotation applied by `kCAAnimationRotateAuto'. You need to remove the rotation from your transform before applying the animation.
Of course that means you need to reapply the transformation when the animation completes. And of course you want to do that without seeing any flicker in the appearance of the image. That's not hard, but there some secret sauce involved, which I explain below.
You presumably want your spaceship to start out pointing along the tangent of the path, even when the spaceship is sitting still having not been animated yet. If your spaceship image is pointing to the right, but your path goes up, then you need to set the transform of the image to include a 90° rotation. But perhaps you don't want to hardcode that rotation - instead you want to look at the path and figure out its starting tangent.
I'll show some of the important code here. You can find my test project on github. You may find some use in downloading it and trying it out. Just tap on the green “spaceship” to see the animation.
So, in my test project, I have connected my UIImageView to an action named animate:. When you touch it, the image moves along half of a figure 8 and doubles in size. When you touch it again, the image moves along the other half of the figure 8 (back to the starting position), and returns to its original size. Both animations use kCAAnimationRotateAuto, so the image points along the tangent of the path.
Here's the start of animate:, where I figure out what path, scale, and destination point the image should end up at:
- (IBAction)animate:(id)sender {
UIImageView* theImage = self.imageView;
UIBezierPath *path = _isReset ? _path0 : _path1;
CGFloat newScale = 3 - _currentScale;
CGPoint destination = [path currentPoint];
So, the first thing I need to do is remove any rotation from the image's transform, since as I mentioned, it will interfere with kCAAnimationRotateAuto:
// Strip off the image's rotation, because it interferes with `kCAAnimationRotateAuto`.
theImage.transform = CGAffineTransformMakeScale(_currentScale, _currentScale);
Next, I go into a UIView animation block so that the system will apply animations to the image view:
[UIView animateWithDuration:3 animations:^{
I create the keyframe animation for the position and set a couple of its properties:
// Prepare my own keypath animation for the layer position.
// The layer position is the same as the view center.
CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
positionAnimation.path = path.CGPath;
positionAnimation.rotationMode = kCAAnimationRotateAuto;
Next is the secret sauce for preventing flicker at the end of the animation. Recall that animations do not effect the properties of the “model layer“ that you attach them to (theImage.layer in this case). Instead, they update the properties of the “presentation layer“, which reflects what's actually on the screen.
So first I set removedOnCompletion to NO for the keyframe animation. This means the animation will stay attached to the model layer when the animation is complete, which means I can access the presentation layer. I get the transform from the presentation layer, remove the animation, and apply the transform to the model layer. Since this is all happening on the main thread, these property changes all happen in one screen refresh cycle, so there's no flicker.
positionAnimation.removedOnCompletion = NO;
[CATransaction setCompletionBlock:^{
CGAffineTransform finalTransform = [theImage.layer.presentationLayer affineTransform];
[theImage.layer removeAnimationForKey:positionAnimation.keyPath];
theImage.transform = finalTransform;
}];
Now that I've set up the completion block, I can actually change the view properties. The system will automatically attach animations to the layer when I do this.
// UIView will add animations for both of these changes.
theImage.transform = CGAffineTransformMakeScale(newScale, newScale);
theImage.center = destination;
I copy some key properties from the automatically-added position animation to my keyframe animation:
// Copy properties from UIView's animation.
CAAnimation *autoAnimation = [theImage.layer animationForKey:positionAnimation.keyPath];
positionAnimation.duration = autoAnimation.duration;
positionAnimation.fillMode = autoAnimation.fillMode;
and finally I replace the automatically-added position animation with the keyframe animation:
// Replace UIView's animation with my animation.
[theImage.layer addAnimation:positionAnimation forKey:positionAnimation.keyPath];
}];
Double-finally I update my instance variables to reflect the change to the image view:
_currentScale = newScale;
_isReset = !_isReset;
}
That's it for animating the image view with no flicker.
And now, as Steve Jobs would say, One Last Thing. When I load the view, I need to set the transform of the image view so that it's rotated to point along the tangent of the first path that I will use to animate it. I do that in a method named reset:
- (void)reset {
self.imageView.center = _path1.currentPoint;
self.imageView.transform = CGAffineTransformMakeRotation(startRadiansForPath(_path0));
_currentScale = 1;
_isReset = YES;
}
Of course, the tricky bit is hidden in that startRadiansForPath function. It's really not that hard. I use the CGPathApply function to process the elements of the path, picking out the first two points that actually form a subpath, and I compute the angle of the line formed by those two points. (A curved path section is either a quadratic or cubic bezier spline, and those splines have the property that the tangent at the first point of the spline is the line from the first point to the next control point.)
I'm just going to dump the code here without explanation, for posterity:
typedef struct {
CGPoint p0;
CGPoint p1;
CGPoint firstPointOfCurrentSubpath;
CGPoint currentPoint;
BOOL p0p1AreSet : 1;
} PathState;
static inline void updateStateWithMoveElement(PathState *state, CGPathElement const *element) {
state->currentPoint = element->points[0];
state->firstPointOfCurrentSubpath = state->currentPoint;
}
static inline void updateStateWithPoints(PathState *state, CGPoint p1, CGPoint currentPoint) {
if (!state->p0p1AreSet) {
state->p0 = state->currentPoint;
state->p1 = p1;
state->p0p1AreSet = YES;
}
state->currentPoint = currentPoint;
}
static inline void updateStateWithPointsElement(PathState *state, CGPathElement const *element, int newCurrentPointIndex) {
updateStateWithPoints(state, element->points[0], element->points[newCurrentPointIndex]);
}
static void updateStateWithCloseElement(PathState *state, CGPathElement const *element) {
updateStateWithPoints(state, state->firstPointOfCurrentSubpath, state->firstPointOfCurrentSubpath);
}
static void updateState(void *info, CGPathElement const *element) {
PathState *state = info;
switch (element->type) {
case kCGPathElementMoveToPoint: return updateStateWithMoveElement(state, element);
case kCGPathElementAddLineToPoint: return updateStateWithPointsElement(state, element, 0);
case kCGPathElementAddQuadCurveToPoint: return updateStateWithPointsElement(state, element, 1);
case kCGPathElementAddCurveToPoint: return updateStateWithPointsElement(state, element, 2);
case kCGPathElementCloseSubpath: return updateStateWithCloseElement(state, element);
}
}
CGFloat startRadiansForPath(UIBezierPath *path) {
PathState state;
memset(&state, 0, sizeof state);
CGPathApply(path.CGPath, &state, updateState);
return atan2f(state.p1.y - state.p0.y, state.p1.x - state.p0.x);
}
Yow mention that you kick off the animation with "rotationMode set to YES", but the documentation states that rotationMode should be set using an NSString...
In particular:
These constants are used by the rotationMode property.
NSString * const kCAAnimationRotateAuto
NSString * const kCAAnimationRotateAutoReverse
Have you tried setting:
keyframe.animationMode = kCAAnimationRotateAuto;
The documentation states:
kCAAnimationRotateAuto: The objects travel on a tangent to the path.

Resources