I'm trying to rotate an image layer by given angle, but it is showing a really weird behavior in iOS 7. It works as expected in iOS 8 and I can't figure out what I'm doing wrong.
Here's the expected image (iOS 8 result). (45 degrees rotated)
This is what I get from iOS 7.
The arrow is supposed to have the same center as the image, but it moves to an unexpected position.
Here's my code. (initial translate and anchor point is to adjust the rotating center because the arrow image's rotation should not happen around the actual center of that image -- and even without these lines, it doesn't make any difference in iOS 7)
if (CATransform3DIsIdentity(self.compassImageView.layer.transform)) {
self.compassImageView.layer.transform = CATransform3DTranslate(CATransform3DIdentity, 0, 5, 0);
}
self.compassImageView.layer.anchorPoint = CGPointMake(0.5, 95 / 180.0);
NSString *zRotationKeyPath = #"transform.rotation.z";
[self.compassImageView.layer setValue:#(targetAngle * M_PI / 180.0) forKeyPath:zRotationKeyPath];
Thanks!
Related
I'm creating a UIButton and adding it above a UITabBar, in a custom UITabBarController.
var button = new UIButton(new CGRect(x, y, 50, 50));
button.Layer.BorderWidth = 2;
button.Layer.CornerRadius = button.Frame.Height / 2;
button.SetImage(UIImage.FromBundle("Name"), UIControlState.Normal);
this.View.AddSubview(button);
this.View.LayoutIfNeeded();
When I try to add a rotation transform on the image view, it doesn't rotate around the center and z axis, as I'd expect (and found in every documentation I read).
Instead, it animates somehow different.
It's the same regardless if I add the transform on the ImageView or its layer.
This for example is a rotation of 30 degrees (pi / 180 * 30) and 10 degrees.
However, when I add a SublayerTransform to the button layer, it transforms as expected. The problem with that approach is that it is not animateable with UIView.Animate or CATransaction.Begin.
Position, AnchorPoint and everything else is set to default. I tried playing around with these properties too, but to no avail.
As you can see from the images, it's a + sign that should animate to become an x.
Any hint is very much appreciated.
The 3D transforms I have defined like this:
CATransform3D.MakeRotation((float)(Math.PI / 180 * 45), 0, 0, 1)
and the normal ones like this:
CGAffineTransform.MakeRotation((float)(Math.PI / 180 * 45))
If you need to rotate just the button image view for some reason, try this:
button.ImageView.Layer.Transform = CATransform3D.MakeRotation((float)(Math.PI / 180 * 45), 0.0001f, 0.0001f, 1);
With the x and y values set to 0, it seems that there is still rotation on the x and y axis, though there should not be. With a value of 45 degrees, it rotates the x and y as well so that you are seeing the image edge on (so seems to be rotating x and y by 90 degrees), i.e. it disappears. Putting in a small value for X and y seems to avoid this error. I am going to see if this is exclusive to Xamarin or if it is an iOS thing.
EDIT: I just tested this in Obj-C in XCode and got the exact same behavior, so this is not a Xamarin issue but is an issue with iOS and how it is processing transforms for the UIImageView that is in a UIButton. Same issue does not occur with a UIImageView that is not in the UIButton.
I am trying to implement a custom painter that can draw an image (scaled down version) on the canvas and the drawn image can be rotated and scaled.
I get to know that to scale the image I have to scale the canvas using scale method.
Now the questions is how to rotate the scaled image on its center (or any other point). The rotate method of canvas allow only to rotate on top left corner.
Here is my implementation that can be extended
Had the same problem, Solution was simply making your own rotation method in three lines
void rotate(Canvas canvas, double cx, double cy, double angle) {
canvas.translate(cx, cy);
canvas.rotate(angle);
canvas.translate(-cx, -cy);
}
We thus first move the canvas towards the point you want to pivot around. We then rotate along the the topleft (default for Flutter) which in coordinate space is the pivot you want and then put the canvas back to the desired position, with the rotation applied. Method is very efficient, requiring only 4 additions for the translation and the rotation cost is identical to the original one.
This can achieve by shifting the coordinate space as illustrated in figure 1.
The translation is the difference in coordinates between C1 and C2, which are exactly as between A and B in figure 2.
With some geometry formulas, we can calculate the desired translation and produce the rotated image as in the method below
ui.Image rotatedImage({ui.Image image, double angle}) {
var pictureRecorder = ui.PictureRecorder();
Canvas canvas = Canvas(pictureRecorder);
final double r = sqrt(image.width * image.width + image.height * image.height) / 2;
final alpha = atan(image.height / image.width);
final beta = alpha + angle;
final shiftY = r * sin(beta);
final shiftX = r * cos(beta);
final translateX = image.width / 2 - shiftX;
final translateY = image.height / 2 - shiftY;
canvas.translate(translateX, translateY);
canvas.rotate(angle);
canvas.drawImage(image, Offset.zero, Paint());
return pictureRecorder.endRecording().toImage(image.width, image.height);
}
alpha, beta, angle are all in radian.
Here is the repo of the demo app
If you don't want to rotate the image around the center of the image you can use this way. You won't have to care about what the offset of the canvas should be in relation to the image rotation, because the canvas is moved back to its original position after the image is drawn.
void rotate(Canvas c, Image image, Offset focalPoint, Size screenSize, double angle) {
c.save();
c.translate(screenSize.width/2, screenSize.height/2);
c.rotate(angle);
// To rotate around the center of the image, focal point is the
// image width and height divided by 2
c.drawImage(image, focalPoint*-1, Paint());
c.translate(-screenSize.width/2, -screenSize.height/2);
c.restore();
}
I'm trying to create a paper folding effect in Swift using CALayers and CATransform3DRotate. There are some libraries out there, but those are pretty outdated and don't fit my needs (they don't have symmetric folds, for example).
My content view controller will squeeze to the right half side of the screen, revealing the menu at the left side.
Everything went well, until I applied perspective: then the dimensions I calculate are not correct anymore.
To explain the problem, I created a demo to show you what I'm doing.
This the content view controller with three squares. I will use three folds, so each square will be on a separate fold.
The even folds will get anchor point (0, 0.5) and the odd folds will get anchor point (1, 0.5), plus they'll receive a shadow.
When fully folded, the content view will be half of the screen's width.
On an iPhone 7, each fold/plane will be 125 points unfolded and 62.5 points fully folded when looked at.
To calculate the rotation needed to achieve this 62.5 points width, we can use a trigonometric function. To illustrate, look at this top-down view:
We know the original plane size (125) and the 2D width (62.5), so we can calculate the angle α using arccos:
let angle = acos(width / originalWidth)
The result is 1.04719755 rad or 60 degrees.
When using this formula with CATransform3DRotate, I get the correct result:
Now for the problem: when I add perspective, my calculation isn't correct anymore. The planes are bigger. Probably because of the now different projection.
You can see the planes are now overlapping and being clipped.
I reconstructed the desired result on the right by playing with the angle, but the correction needed is not consistent, unfortunately.
Here's the code I use. It works perfectly without perspective.
// Loop layers
for i in 0..<self.layers.count {
// Get layer
let layer = self.layers[i]
// Get dimensions
let width = self.frame.size.width / CGFloat(self.numberOfFolds)
let originalWidth = self.sourceView.frame.size.width / CGFloat(self.numberOfFolds)
// Calculate angle
let angle = acos(width / originalWidth)
// Set transform
layer.transform = CATransform3DIdentity
layer.transform.m34 = 1.0 / -500
layer.transform = CATransform3DRotate(layer.transform, angle * (i % 2 == 0 ? -1 : 1), 0, 1, 0)
// Update position
if i % 2 == 0 {
layer.position = CGPoint(x: (width * CGFloat(i)), y: layer.position.y)
} else {
layer.position = CGPoint(x: (width * CGFloat(i + 1)), y: layer.position.y)
}
}
So my question is: how do I achieve the desired result? Do I need to correct the angle, or should I calculate the projected/2D width differently?
Thanks in advance! :)
I'm having trouble understanding how I make my SKSpriteNode fit each iPhone. I have a background image and a separate chair image that needs to be in the same position and scalling for each iPhone. When I get it to look good on say the 6+ if I swap to a 5s its not in the correct position and isn't scaled for that iPhone. This is how I am currently scaling and positioning my SKSpriteNode.
chair.position = CGPointMake(self.size.width * 0.50, self.size.height * 0.35)
chair.setScale(0.50)
chair.zPosition = 1.0
self.addChild(Chair)
I guess you have a problem of screen scale factors which are different from iPhone 5s (x2) to iPhone 6+ (x3). Simply multiply with screen factor you may solve the problem. The code (Swift) looks like bellow:
let screenScaleFactor = UIScreen.mainScreen().scale
chair.position = CGPointMake(self.size.width * 0.50 * screenScaleFactor, self.size.height * 0.35 * screenScaleFactor)
chair.setScale(0.50 * screenScaleFactor)
chair.zPosition = 1.0
self.addChild(Chair)
i'm trying to build a game in xna, i got a circle which i want the player to move around it, as you can see in the following picture, its working great except the drawing part which i'm not pleased with
here's a link to an image http://s12.postimage.org/poiip0gtp/circle.png
i want to center the player object to the edge of the circle so it won't look like the player is standing on air
this is how i calculate the position of the player
rad = (degree * Math.PI / 180);
rotationDegree = (float)((Math.PI * degree) / 180);
currentPosition.X = (float)(Math.Cos(rad) * Earth.radius + (GraphicsDevice.Viewport.Width / 2));
currentPosition.Y = (float)(Math.Sin(rad) * Earth.radius + (GraphicsDevice.Viewport.Height / 2));
and this is how i draw the player
spriteBatch.Draw(texture,currentPosition, null, Color.White,rotationDegree, Vector2.Zero,1f,SpriteEffects.None, 1f);
thank you.
Use the origin overload for spritebatch. Which is where the sprite is drawn according to the position.
Spritebatch.Draw(texture,Position, null,Color.White,0f,new Vector2(texture.Width / 2,texture.Height /2),1f,SpriteEffects.None, 0);
Using texture.Width / 2,texture.Height /2 for origin will center it.
It looks like what you want to do here is adjust the sprite's origin, which is the vector that you're passing into SpriteBatch.Draw(). This is used to determine the "center point" of your sprite; {0, 0} represents the sprite's upper-left corner, while {spriteWidth, spriteHeight} represents the bottom-right corner. Your sprite will be positioned and rotated relative to this origin.