I have 1 view, in this view I have 1 UIImageView and UILabel, when I rotate my view half of view disappear.
This is my code
viewToAnimate is the view cointains image and label.
CATransform3D _3Dt = CATransform3DRotate(viewToAnimate.layer.transform, DEGREES_TO_RADIANS(beginValue), 0.0, 1.0, 0);
_3Dt.m34 = 1.0 / -300;
viewToAnimate.layer.transform = _3Dt;
CATransform3D _scale;
if (needZoom) {
_scale = CATransform3DScale(viewToAnimate.layer.transform , 1+scale, 1+scale, 1+scale);
if (_scale.m11 > 1) {
_scale.m11 = 1;
_scale.m22 = 1;
_scale.m33 = 1;
}
}
else {
_scale = CATransform3DScale(viewToAnimate.layer.transform , 1-scale, 1-scale, 1-scale);
}
viewToAnimate.layer.transform = _scale;
//viewToAnimate.layer.zPosition = sin(DEGREES_TO_RADIANS(beginValue)*MOSAIC_WIDTH)+1;
What is the value of DEGREES_TO_RADIANS? Is it by any chance M_PI/2?
Your code tells that you are rotating around y-axis. See last 3 parameters in first line:
0.0, 1.0, 0
If you are supplying this then this is expected behavior - consider a plane facing you and imagine it rotate around y-axis by +/- 90 degrees, it will definitely disappear.
As mentioned in Diego's comment on the other answer this can be caused by zPosition issues. Make your view above all the others by setting it's zPosition to a higher value.
Related
A have an issue with UIProgressView value. I'm trying to embed ProgressView into Table View cell.
This code works - ProgressView fills it's cell. But there's problem with filling ProgressView itself - it's value rounds to 0.5 (e.g. if value is 0.2 then progressView is not filled, if 0.8 - then it filled 100%, see the image bellow)
Can anyone help with making things right?
CGAffineTransform transform = CGAffineTransformMakeScale(cell.frame.size.width, cell.frame.size.height);
cell.attendanceProgress.transform = transform;
[cell.attendanceProgress setAlpha:0.5];
[cell setUserInteractionEnabled:NO];
float progr = (float)attended / (float)classes.count;
if (classes.count == 0) { progr = 0; }
[cell.attendanceProgress setProgress:progr];
Use
cell.attendanceProgress.frame=CGRectMake(0,0,cell.attendanceProgress.frame.size.width, cell.frame.size.height);
CATransform3D transform = CATransform3DScale(cell.attendanceProgress.layer.transform, 1.0f,cell.frame.size.height, 1.0f);
cell.attendanceProgress.layer.transform = transform;
Make sure set frame before transform.
So I have a UITextView that's supposed to be visually sitting on the bottom edge of the screen and then stretching up and back "into" the screen, "Star Wars" opening crawl-style.
After much googling etc, I feel like I have what looks like the right code for the job... but instead of setting up the perspective view I was looking for, this is just making the UITextView totally disappear!
The text view is set up in a storyboard with springs/struts (no autolayout) such that it's pinned at the top and bottom of the main view, about 20px in from each side, and the springs are active in both directions. Its outlet is hooked up to self.infoTextView. It shows up as I'd expect if I don't apply any transformations to it.
But when i fire off the code below in viewDidLoad, the text view just disappears completely. I'm sure I'm missing something but I can't seem to figure out what t is.
CGRect frame = self.infoTextView.layer.frame;
self.infoTextView.layer.anchorPoint = CGPointMake(.5f, 1.0f);
self.infoTextView.layer.frame = frame;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 1.57, 0, 1, 0);
self.infoTextView.layer.transform = rotationAndPerspectiveTransform;
thanks!
There are two things you'll need to modify in your code:
The rotation axis. You're rotating it around the Y axis. To get the
effect you're after you will need to change the rotate transform to
turn around the X axis.
To get "Star Wars opening crawl-style" effect you'll need to set a negative angle or a negative perspective.
Also, you would probably want to set a more "strong" perspective to achieve a more dramatic effect.
Here's an example based on your transform code:
CGFloat angle = -45;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 200;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, angle / 180.0 * M_PI, 1, 0, 0);
self.infoTextView.layer.transform = rotationAndPerspectiveTransform;
I've never had this happen before and can't figure out what's going on. I suspect it might be auto-layout, but I don't see how. I have a "Compass" view that has several subviews it manages itself (not part of auto layout). Here's an example of their setup:
- (ITMView *) compass {
if (!_compass){
_compass = [ITMView new];
_compass.backgroundColor = [UIColor blueColor];
_compass.layer.anchorPoint = CGPointMake(.5, .5);
_compass.translatesAutoresizingMaskIntoConstraints = NO;
_compass.frame = self.bounds;
__weak ITMCompassView *_self = self;
_compass.onDraw = ^(CGContextRef ref, CGRect frame) { [_self drawCompassWithFrame:frame]; };
[self addSubview:_compass];
}
return _compass;
}
I need to rotate the compass in response to heading changes:
- (void) setCurrentHeading:(double)currentHeading{
_currentHeading = fmod(currentHeading, 360);
double rad = (M_PI / 180) * _currentHeading;
self.compass.transform = CGAffineTransformMakeRotation(rad);
}
The problem is that it's rotating in on the z-axis for some reason:
I'm not manipulating layer transforms on any other views. Does anyone have any idea why this is occurring?
Update
I checked the transform for all superviews. Every superview has an identity transform.
I logged the transform of the compass view before and after it was set for the first time. Before it was set, the transform was at identity, which is expected. After I set the transform to rotate 242.81 degrees (4.24 rad) I get:
[
-0.47700124155378526, -0.87890262006444575,
0.87890262006444575, -0.47700124155378526,
0, 0
]
Update 2
I checked CATransform3DIsAffine and it always returns YES. I double checked the layer transform and for a rotation of 159.7 (degrees) I get:
[
-0.935, 0.356, 0, 0,
-0.356, -0.935, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
That looks correct to me.
All of the transforms are correct but it's still not displaying correctly on screen.
Update 3
I removed my drawing code from the view and set the view background to blue. The view is definitely being rotated, squeezed, or something:
Some things to note:
The view displays correctly at 90, 180, 270 & 0 degrees.
The view disappears (turned on edge) at 45, 135, 225 & 315 degrees.
The view looks like it's being rotated in 3D as it progresses from 0 to 360 degrees.
I'm not sure why #matt withdrew his answer, but he was correct: The compass view had it's frame reset every time I made a rotation in the layoutSubviews method in my containing superview. I wasn't expecting this, thinking that a rotation wouldn't trigger a layoutSubviews. The frame never changed, but the applied transform distorted the results as the frame was re-applied to the view. What threw me was the results really looked like the view was being rotated in 3D, which led me down that particular rabbit hole. At least I know what to look for now.
Something I want to point out: The apparent 3D rotation was very particular. It rotated around each diagonal combination of {x,Y} sequentially between each 90° quadrant of the unit circle. This makes sense if you think about how the frame would distort over those periods.
The solution is simple enough, store and remove the transform before setting the subview frame and then reapply the transform. However, because the rotation is applied very, very frequently (inside an animation block no less) I added an escape to help reduce the load:
- (void) layoutSubviews{
[super layoutSubviews];
if (!CGRectEqualToRect(_lastLayout, self.bounds)){
CGRect frame = SquareRectAndPosition(self.bounds, CGRectXCenter, CGRectYCenter);
CGAffineTransform t;
t = self.compass.transform;
self.compass.transform = CGAffineTransformIdentity;
self.compass.frame = frame;
self.compass.transform = t;
t = self.target.transform;
self.target.transform = CGAffineTransformIdentity;
self.target.frame = frame;
self.target.transform = t;
}
_lastLayout = self.bounds;
}
I'm trying to create a "page flip effect" using UIView instead of CALayer due to a project limitation. This requires flipping 1 UIView 180 degrees and essentially "sticking it" to the back of the other UIView. You then rotate the two UIViews simultaneously by rotating the superview in 3D space.
I'm trying to port AFKPageFlipper's "initFlip" method to use UIView instead of UIImage.
Below is a snippet of my attempt to port it. The initial page flip works, but the "front layer" in the code doesn't seem to show up. As if I"m not able to see the backend of the page. When I'm flipping the page, the animation is initially correct (back layer is fine), but then the other side of the page (front layer), I see the inverted view of the first page (backLayer).
Any help would be awesome!
flipAnimationLayer = [[UIView alloc] init];
flipAnimationLayer.layer.anchorPoint = CGPointMake(1.0, 0.5);
flipAnimationLayer.layer.frame = rect;
[self addSubview:flipAnimationLayer];
UIView *backLayer;
UIView *frontLayer;
if (flipDirection == AFKPageFlipperDirectionRight)
{
backLayer = currentViewSnap2;
backLayer.layer.contentsGravity = kCAGravityLeft;
frontLayer = nextViewSnap2;
frontLayer.layer.contentsGravity = kCAGravityRight;
}else
{
backLayer = nextViewSnap2;
backLayer.layer.contentsGravity = kCAGravityLeft;
frontLayer= currentViewSnap2;
frontLayer.layer.contentsGravity = kCAGravityRight;
}
backLayer.frame = flipAnimationLayer.bounds;
backLayer.layer.doubleSided = NO;
backLayer.clipsToBounds = YES;
[flipAnimationLayer addSubview:backLayer];
frontLayer.frame = flipAnimationLayer.bounds;
frontLayer.layer.doubleSided = NO;
frontLayer.clipsToBounds = YES;
frontLayer.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1.0, 0);
[flipAnimationLayer addSubview:frontLayer];
if (flipDirection == AFKPageFlipperDirectionRight)
{
CATransform3D transform = CATransform3DMakeRotation(0.0, 0.0, 1.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.layer.transform = transform;
currentAngle = startFlipAngle = 0;
endFlipAngle = -M_PI;
} else
{
CATransform3D transform = CATransform3DMakeRotation(-M_PI / 1.1, 0.0, 1.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.layer.transform = transform;
currentAngle = startFlipAngle = -M_PI;
endFlipAngle = 0;
}
Your code is rotating layers, not views. That's fine.
I would not expect the code you posted to animate, since a layer's backing view doesn't do implicit animation, You could make it animate by using a CABasicAnimation. Or, you could create layers for your front and back views and attach them as sublayers of your view's layers. If you do that than manipulating the transform on the layers will use implicit animations.
What I've done to create my own font-to-back flip as you describe is to fake it.
I animate in 2 steps: First from zero degrees (flat) to 90 degrees (where the layers become invisible.) At that moment I hide the first layer and make the second layer visible, rotated 90 degrees the other way, and then rotate the other layer back to zero. This creates the same visual effect as showing the back face of the rotation.
If you use implicit layer animation to do this you'll need to put the changes to the transform inside a CATransaction block and set the animation timing to linear, or use ease-in for the first half and ease-out for the second half. That's because animations default to ease-in,ease-out timing, and the first animation to 90 degrees will slow down at the end, and then the second 90 degree animation will ease in.
I've been working on a maps replacement for quite a while now. The whole thing works with a UIScrollView backed by a CATiledLayer.
To rotate my map, i rotate the layer itself. (Using CATransform3DMakeRotation) Works pretty well so far =)
But if I ever call setZoomScale method the CATransform3D that is going to be submitted to my layer is resetting the rotation to 0.
My question is, is there any way to set the zoomscale of my scrollView without loosing the applied rotation?
The same problem exists for the pinch gestures.
//Additional Infos
To rotate around the current Position, i have to edit the anchor point. Maybe this is a problem for the scaling, too.
- (void)correctLayerPosition {
CGPoint position = rootView.layer.position;
CGPoint anchorPoint = rootView.layer.anchorPoint;
CGRect bounds = rootView.bounds;
// 0.5, 0.5 is the default anchorPoint; calculate the difference
// and multiply by the bounds of the view
position.x = (0.5 * bounds.size.width) + (anchorPoint.x - 0.5) * bounds.size.width;
position.y = (0.5 * bounds.size.height) + (anchorPoint.y - 0.5) * bounds.size.height;
rootView.layer.position = position;
}
- (void)onFinishedUpdateLocation:(CLLocation *)newLocation {
if (stayOnCurrentLocation) {
[self scrollToCurrentPosition];
}
if (rotationEnabled) {
CGPoint anchorPoint = [currentConfig layerPointForLocation:newLocation];
anchorPoint.x = anchorPoint.x / rootView.bounds.size.width;
anchorPoint.y = anchorPoint.y / rootView.bounds.size.height;
rootView.layer.anchorPoint = anchorPoint;
[self correctLayerPosition];
}
}
You can implement scrollViewDidZoom: delegate method and concatenate the two transforms to achieve desired effect:
- (void) scrollViewDidZoom:(UIScrollView *) scrollView
{
CATransform3D scale = contentView.layer.transform;
CATransform3D rotation = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
contentView.layer.transform = CATransform3DConcat(rotation, scale);
}
EDIT
I've got simpler idea! How about adding another view to the hierarchy with the rotation transform attached? Here's the proposed hierarchy:
ScrollView
ContentView - the one returned by viewForZoomingInScrollView:
RotationView - the one with rotation transform
MapView - the one with all the tiles
I don't think that performance should be any concern here and it's worth trying.