I have the code that will rotate my image like a "spinner" based on its geometric center... however because the image itself is asymmetrical, i wish to rotate it about a particular reference point in terms of (x,y) coordinates of the UIImageView.
Here is my current code that rotates the image:
self.spinnerView.center=CGPointMake(self.spinnerView.center.x, self.spinnerView.center.y);
self.spinnerView.transform=CGAffineTransformMakeRotation (angle);
// why u change center of View on itself??? o_O What you want with it?
self.spinnerView.center=CGPointMake(self.spinnerView.center.x, self.spinnerView.center.y);
And read a documentation:
The origin of the transform is the value of the center property, or
the layer’s anchorPoint property if it was changed. (Use the layer
property to get the underlying Core Animation layer object.) The
default value is CGAffineTransformIdentity.
so :
self.spinnerView.layer.anchorPoint=CGPointMake(0, 0);
self.spinnerView.transform=CGAffineTransformMakeRotation(angle);
was written about anchorPoint:
You specify the value for this property using the unit coordinate
space. The default value of this property is (0.5, 0.5), which
represents the center of the layer’s bounds rectangle. All geometric
manipulations to the view occur about the specified point.
Related
I have a custom view, which I use for markup (draw lines and other figures on it). The view controller recognizes touches and gestures and passes info to the view so it can draw itself properly. Each figure has a label (CATextLayer) with some figure info on it (line length for example).
I added a rotation gesture recognizer to the view controller to rotate this drawing view. I want to rotate the view, but prevent labels from rotation (so they stay 0.0 degrees relative to the superview). For this I calculate the new drawing view's angle relative to the superview and set label's angle property to the negative value, so I can rotate them oppositely. For example, the view is rotated 30 degrees, then I rotate labels -30 degrees.
In the view drawing method which is responsible for drawing figures and setting the labels, I create new transform for each label each time the view needs to be redrawn:
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, self.angle); //self.angle - rotation angle in radians
transform = CGAffineTransformScale(transform, scale, scale); //scale label (need this for pinch gesture, works as expected)
label.transform = transform;
All this stuff works if I rotate labels 90 degrees. If the degree is not 0, 90, 180, etc, the label's frame changes: shrinks or enlarges, with large angles it even disappears. I understand, that when you rotate the rectangle, it's frame should get bigger as it's pointed here: Why after rotating UIImageView size is getting changed?
Is there a way to prevent CATextLayer from changing its shape when rotating?
Normal position of label:
When rotated 90 degrees (everything is nice)
Rotated to some arbitrary angle (label frame is misshapen)
The issue was occurring because I was resetting CATextLayer's origin after setting its transform. According to the transform property documentation:
When the value of this property is anything other than the identity transform, the value in the frame property is undefined and should be ignored.
To change the position of a layer, use its position property.
The frame isn't that what you expected after a non-rectangular rotation.
I see three possible solutions for your problem:
Calculate the correct size of your text, and set the frame by assigning appropriate values to position and bounds.
Create a plain CALayer for the background of each text layer, if the first solution doesn't help.
But it should be much easier if you're rotate the image only and keep the rotation of the text layers unchanged. You can achieve this by adding the text layers to the super layer of the image layer. With this setup you have just to calculate the new position of the text layers after rotation.
I'm very unsure whether the first two solutions are practicable, and I would prefer the third one.
When I change SKShapeNode's .alpha, its stroke shows itself as if .strokeColor of node was brighter than its .fillColor. Setting .lineWidth to 0 canceles antialiased smotheness that the stroke provides. Same does setting .strokeColor to SKColor.clearColor.
Question
What is the best way to make stroke look the same as fill when changing node.alpha, preserving the antialiased smootheness of edges?
Though it is possible to set alpha of node's stroke directly by getting its' colors' HSBA and reassigning a new color with different alpha to the stroke (for more info look this answer), as the node's fill covers node's stroke, stroke's alpha must be set to 0 so that it actually wouldn't be seen. But this canceles antialiasing.
So it appears, that the only way to achieve smooth node's edges with no stroke be seen, is to convert SKShapeNode to SKSpriteNode and reset sprite.alpha to desired.
Here is a code snippet in Swift
let sprite = SKSpriteNode(texture: self.view!.textureFromNode(shapeNode))
Its' not that bad, because, according to Apple's docs
The node being rendered does not need to appear in the view’s
presented scene.
However, you still have to have access to the view from where the conversion takes place.
In CALayer's API, 'position' is used for setting the position of the layer.
By my own testing, setting bounds.origin does not do anything. Am I missing something?
The bounds.origin controls where the origin of the layer's coordinate system is, relative to the layer's frame in its superlayer. Changing it has two visible effects:
The position of sublayers of the layer. For example, when you scroll a UIScrollView, the scroll view doesn't change its subview's frames. It simply changes its bounds.origin. I suggest setting up a toy app with a scroll view and doing NSLog("scroll view bounds = %#", NSStringFromCGRect(scrollView.bounds)); from a timer or some other trigger to get a sense of what's happening.
The origin of the graphics context coordinate system in drawInContext:. Mostly commonly you would see this effect in a view's drawRect: method. Your CGContext inside drawRect: will have been translated by the self.bounds.origin.
You may find it helpful to read about “View Geometry and Coordinate Systems” in the View Programming Guide for iOS and “Layer Objects Define Their Own Geometry” in the Core Animation Programming Guide, although really neither of them have a good discussion of the bounds origin.
Changing the bounds rectangle changes the position and size of the content in the coordinate system of the layer itself. Changing the frame (or position) changes the position of the layer in the coordinate system of its super layer. Usually you only want to change the frame, not the bounds.
I am rotating the image on slider value -
I am using this code for rotation -
editingView.transform = CGAffineTransformRotate(editingView.transform,sliderVal);
its Rotating properly but if i am trying to move or resize after rotation,The editingView is resizing with unexpected behavior and view disappears from screen.
Please suggest me what i am doing wrong.
Well whenever you rotate a view which is inside a superview, you should preserve the position of the view. If you are not rotating any view across the origin then, you should first translate the view's origin to the superview's origin and then rotate and then again translate back to the original point.
Find the coordinate if the view you want to rotate with respect to its superview, say it (x,y).
Translate the view to the origin as;
view.transform = CGAffineTransformMakeTranslation(x,y)
Rotate the view by some angle, say PI,
view.transform = CGAffineTransformMakeRotation(PI)
After rotation translate back to the original point as;
view.transform = CGAffineTransformMakeTranslation(-x,-y)
And that's it. It should work with all the different rotation.
Have a look at this question.
Since you do not show any code on how you do the move or resizing, I suspect you are not properly concatenating the transforms. Furthermore, after you did the rotation, a translation will possibly work on the rotated coordinate system, therefore leading to unexpected behaviour.
Changing transform value affecting the frame value of UIView. As Apple says:
Warning: If this property is not the identity transform, the value of
the frame property is undefined and therefore should be ignored.
Apple docs
So, if you are moving or resizing your view using frame property, try do this with bounds, center properties
Can somebody point me to a good primer on the above, and what happens to one when you mess with the others? It seems as though no matter what I do, once I start messing with either the status bar orientation or the view transform (even if all I'm doing is 90-degree rotations), I can count on my views ending up sideways, upside down and backwards, and on a frustrating afternoon of trial and error trying to get them straightened out. I'm sure it all makes sense once you know the logic and what order everything's applied in, but so far, empirically, I haven't been able to figure it out.
I don't know of a good single document primer on the subject, but the following is what I've learned from experience and reading the docs.
center, bounds, and frame
If you set frame then center and bounds will be updated. If you set center or bounds then frame will be updated. Frame is a convenience method for manipulating center and bounds using the superview's coordinate system.
From UIView Class Reference:
The geometry of a view is defined by its frame, bounds, and center properties. The frame defines the origin and dimensions of the view in the coordinate system of its superview and is commonly used during layout to adjust the size or position of the view. The center property can be used to adjust the position of the view without changing its size. The bounds defines the internal dimensions of the view as it sees them and is used almost exclusively in custom drawing code. The size portion of the frame and bounds rectangles are coupled together so that changing the size of either rectangle updates the size of both.
See The Relationship of the Frame, Bounds, and Center Properties for more details.
transform
If you set the transform property to something besides the identity transform, frame is undefined. If you set the transform to something else, you should only manipulate the view geometry using center (to position the view in it's superview) and bounds (to adjust the size of the view). Here's the relevant info from UIView Class Reference:
The origin of the transform is the value of the center property, or the layer’s anchorPoint property if it was changed. (Use the layer property to get the underlying Core Animation layer object.) The default value is CGAffineTransformIdentity.
...
Warning If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
See Coordinate System Transforms for more details.
UIInterfaceOrientation
UIInterfaceOrientation doesn't affect the transform, bounds, center, or frame properties directly. However, when the device orientation changes, the view controller will automatically resize its subview (which will in-turn resize it's subviews and so on).
See Responding to Device Orientation Changes and View Controller View Resizing for more details.