I'm developing an iOS application with latest SDK.
I'm working with camera and I want to make zoom. To do it I have this code:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setUpVideo];
}
- (void)setUpVideo
{
NSLog(#"Set up video");
UIView *view = [self videoPreviewView];
CALayer *viewLayer = [view layer];
[viewLayer setMasksToBounds:YES];
AVCaptureVideoPreviewLayer *newCaptureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:DataExchanger.cameraManager.captureSession];
CGRect bounds = [view bounds];
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
CGAffineTransform t;
if (orientation == UIInterfaceOrientationLandscapeRight)
{
t = CGAffineTransformMakeRotation(-M_PI_2);
bounds = CGRectMake(bounds.origin.x, bounds.origin.y,
bounds.size.height, bounds.size.width);
}
[newCaptureVideoPreviewLayer setFrame:bounds];
[newCaptureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
newCaptureVideoPreviewLayer.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
newCaptureVideoPreviewLayer.affineTransform = t;
[viewLayer insertSublayer:newCaptureVideoPreviewLayer below:[[viewLayer sublayers] objectAtIndex:0]];
CGAffineTransform currentTransform = CGAffineTransformIdentity;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, 2.0f, 2.0f);
view.transform = newTransform;
}
But I see a different between the zoom image and the same image without zoom. Without moving the camera I see that the center shifted to the left bottom when I made zoom.
Maybe I have to move videoView centers with a formula that calculates its new center using the scale.
Do you know how to fix that?
Related
I am animating UIView to have a feeling of book's page effect
#property (strong, nonatomic) UIView *insideView;
#property (strong, nonatomic) UIView *pageView;
#property (strong, nonatomic) UIView *backPageView;
#property (assign, nonatomic) CGRect cardFrame;
- (void)viewDidLoad
{
[super viewDidLoad];
[[self view] setBackgroundColor:[UIColor grayColor]];
//create frame for 2 test views
CGFloat size = 200.0;
_cardFrame = CGRectMake([[self view] center].x - size / 2, [[self view] center].y - size / 2 , size, size);
//lower view
_insideView = [[UIView alloc] initWithFrame: _cardFrame];
[_insideView setBackgroundColor:[UIColor redColor]];
//upper view
_pageView = [[UIView alloc] initWithFrame:_cardFrame];
[_pageView setBackgroundColor:[UIColor greenColor]];
//upper view back side
_backPageView = [[UIView alloc] initWithFrame:_cardFrame];
[_backPageView setBackgroundColor:[UIColor blueColor]];
[[self view] addSubview:_insideView];
[[self view] addSubview:_pageView];
[[self view] insertSubview:_backPageView belowSubview:_pageView];
//get layer of upper view and set needed property
CALayer *viewLayer = [_pageView layer];
CALayer *viewBackLayer = [_backPageView layer];
[viewLayer setAnchorPoint:(CGPoint){0.0 , 0.5}];
[viewLayer setFrame:_cardFrame];
[viewLayer setDoubleSided:NO];
[viewBackLayer setAnchorPoint:(CGPoint){0.0 , 0.5}];
[viewBackLayer setFrame:_cardFrame];
//create perspective
CATransform3D mt = CATransform3DIdentity;
mt.m34 = 1.0/-500.;
//create rotation
CATransform3D open = CATransform3DMakeRotation(3 * M_PI_4,0,-1, 0);
//create result transform
CATransform3D openTransform = CATransform3DConcat(open, mt);
[UIView animateWithDuration:1.0 animations:^
{
//close animation
[viewLayer setTransform:openTransform];
[viewBackLayer setTransform:openTransform];
} completion:^(BOOL finished)
{
[UIView animateWithDuration:1.0 animations:^
{
//close animation
[viewLayer setTransform:CATransform3DIdentity];
[viewBackLayer setTransform:CATransform3DIdentity];
}];
}];
}
But i want the UIView to animate from bottom to top,this code do animate but from left to right like book's page.
Please help!!
Thanks in advance.
try this.
//create frame for 2 test views
CGFloat size = 200.0;
_cardFrame = CGRectMake([[self view] center].x - size / 2, [[self view] center].y - size / 2 , size, size);
//lower view
_insideView = [[UIView alloc] initWithFrame: _cardFrame];
[_insideView setBackgroundColor:[UIColor redColor]];
//upper view
_pageView = [[UIView alloc] initWithFrame:_cardFrame];
[_pageView setBackgroundColor:[UIColor greenColor]];
//upper view back side
_backPageView = [[UIView alloc] initWithFrame:_cardFrame];
[_backPageView setBackgroundColor:[UIColor blueColor]];
[[self view] addSubview:_insideView];
[[self view] addSubview:_pageView];
[[self view] insertSubview:_backPageView belowSubview:_pageView];
//get layer of upper view and set needed property
CALayer *viewLayer = [_pageView layer];
CALayer *viewBackLayer = [_backPageView layer];
[viewLayer setAnchorPoint:(CGPoint){0.5 , 0.0}];
[viewLayer setFrame:_cardFrame];
[viewLayer setDoubleSided:NO];
[viewBackLayer setAnchorPoint:(CGPoint){0.5 , 0.0}];
[viewBackLayer setFrame:_cardFrame];
//create perspective
CATransform3D mt = CATransform3DIdentity;
mt.m34 = 1.0/-500.;
//create rotation
CATransform3D open = CATransform3DMakeRotation(3 * M_PI_4,1,0,0);
//create result transform
CATransform3D openTransform = CATransform3DConcat(open, mt);
[UIView animateWithDuration:1.0 animations:^
{
//close animation
[viewLayer setTransform:openTransform];
[viewBackLayer setTransform:openTransform];
} completion:^(BOOL finished)
{
[UIView animateWithDuration:1.0 animations:^
{
//close animation
[viewLayer setTransform:CATransform3DIdentity];
[viewBackLayer setTransform:CATransform3DIdentity];
}];
}];
Here is the code
- (void)viewDidLoad
{
[super viewDidLoad];
[[self view] setBackgroundColor:[UIColor grayColor]];
//create frame for 2 test views
CGFloat size = 200.0;
_cardFrame = CGRectMake([[self view] center].x - size / 2, [[self view] center].y - size / 2 , size, size);
//lower view
_insideView = [[UIView alloc] initWithFrame: _cardFrame];
[_insideView setBackgroundColor:[UIColor redColor]];
//upper view
_pageView = [[UIView alloc] initWithFrame:_cardFrame];
[_pageView setBackgroundColor:[UIColor greenColor]];
//upper view back side
_backPageView = [[UIView alloc] initWithFrame:_cardFrame];
[_backPageView setBackgroundColor:[UIColor blueColor]];
[[self view] addSubview:_insideView];
[[self view] addSubview:_pageView];
[[self view] insertSubview:_backPageView belowSubview:_pageView];
//get layer of upper view and set needed property
CALayer *viewLayer = [_pageView layer];
CALayer *viewBackLayer = [_backPageView layer];
// need to change the anchor point to center of top edge.
// that is the point in which you need to rotate.
[viewLayer setAnchorPoint:(CGPoint){0.5 , 0.0}];
[viewLayer setFrame:_cardFrame];
[viewLayer setDoubleSided:NO];
[viewBackLayer setAnchorPoint:(CGPoint){0.5 , 0.0}];
[viewBackLayer setFrame:_cardFrame];
//create perspective
CATransform3D mt = CATransform3DIdentity;
mt.m34 = 1.0/-500.;
//need to rotate in X axis. so changed arguments.
CATransform3D open = CATransform3DMakeRotation(3 * M_PI_4,1,0, 0);
//create result transform
CATransform3D openTransform = CATransform3DConcat(open, mt);
[UIView animateWithDuration:1.0 animations:^
{
//close animation
[viewLayer setTransform:openTransform];
[viewBackLayer setTransform:openTransform];
} completion:^(BOOL finished)
{
[UIView animateWithDuration:1.0 animations:^
{
//close animation
[viewLayer setTransform:CATransform3DIdentity];
[viewBackLayer setTransform:CATransform3DIdentity];
}];
}];
}
The two key parts in that page flip is the rotation transform and the anchor point. If you are unfamiliar with this then I suggest that you look at the documentation to see how they work, I will only give a brief explanation.
Rotation transform
You can see that CATransform3DMakeRotation takes 4 arguments, the first is the angle of the rotation and the next three are the axis of the rotation. You are rotating around (0,-1, 0) which is the y axis (vertical on the screen). To instead cause a rotation around the horizontal axis (x) you should change the last 3 arguments to 1, 0, 0 (or maybe -1, I can't seem to remember that on the top of my head).
CATransform3D open = CATransform3DMakeRotation(3.0*M_PI_4, // angle
1, 0, 0); // axis
Anchor point
The anchor point specifies how the layer is drawn relative to its position. Both x and y in the anchor point range from 0 to 1 so (0.5, 0.5) is in the center of the layer, no matter the size. In your case the anchor point is (0.0, 0.5) meaning center left edge. You should probably change that to (0.5, 0.0) center top edge or (0.5, 1.0) center bottom edge depending on where you want the anchor point to be for your page flip.
viewLayer.anchorPoint = CGPointMake(0.5, 0.0); // center top edge
set Uiview Frame in view did Load then set on your event button clicked. view animaate Bottom to top
CGRect optionsFrame = AddNOTEview.frame;
optionsFrame.origin.y=300;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.50];
AddNOTEview.frame = optionsFrame;
[self.view addSubview:AddNOTEview];
I am working with Apple's AVCam example and I am trying to modify the layout to work with the iPad.
I'm trying to get the status bar to be present throughout, however whenever I enable it, (what I believe to be the videoPreviewView and its viewLayer) are in the status bar's way. So I need to move this down by 20px to make room for the status bar but I'm not sure how.
AVCaptureVideoPreviewLayer *newCaptureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[[self captureManager] session]];
UIView *view = [self videoPreviewView];
CALayer *viewLayer = [view layer];
[viewLayer setMasksToBounds:YES];
CGRect bounds = [videoPreviewView bounds];
[[self view] setBounds:bounds];
newCaptureVideoPreviewLayer.bounds = bounds;
[newCaptureVideoPreviewLayer setFrame:bounds];
Put this under UIView *view = [self videoPreviewView];
CGPoint viewCenter = view.center;
viewCenter = CGPointMake(viewCenter.x, viewCenter.y - 20)
view.center = viewCenter;
Adding this to to AdamG's answer did the trick, don't know why or if it is a viable solution. Anyone care to chime in?
CGPoint viewCenter = view.center;
viewCenter = CGPointMake(viewCenter.x, viewCenter.y + 20);
view.center = viewCenter;
CGRect temp = view.frame;
temp.size.height += -20;
view.frame = temp;
I am trying to rotate the rectangle around the circle. So far after putting together some code I found in various places (mainly here: https://stackoverflow.com/a/4657476/861181) , I am able to rotate rectangle around it's center axis.
How can I make it rotate around the circle?
Here is what I have:
OverlaySelectionView.h
#import <QuartzCore/QuartzCore.h>
#interface OverlaySelectionView : UIView {
#private
UIView* dragArea;
CGRect dragAreaBounds;
UIView* vectorArea;
UITouch *currentTouch;
CGPoint touchLocationpoint;
CGPoint PrevioustouchLocationpoint;
}
#property CGRect vectorBounds;
#end
OverlaySelectionView.m
#import "OverlaySelectionView.h"
#interface OverlaySelectionView()
#property (nonatomic, retain) UIView* vectorArea;
#end
#implementation OverlaySelectionView
#synthesize vectorArea, vectorBounds;
#synthesize delegate;
- (void) initialize {
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = NO;
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(rotateVector:)];
panRecognizer.maximumNumberOfTouches = 1;
[self addGestureRecognizer:panRecognizer];
}
- (id) initWithCoder: (NSCoder*) coder {
self = [super initWithCoder: coder];
if (self != nil) {
[self initialize];
}
return self;
}
- (id) initWithFrame: (CGRect) frame {
self = [super initWithFrame: frame];
if (self != nil) {
[self initialize];
}
return self;
}
- (void)drawRect:(CGRect)rect {
if (vectorBounds.origin.x){
UIView* area = [[UIView alloc] initWithFrame: vectorBounds];
area.backgroundColor = [UIColor grayColor];
area.opaque = YES;
area.userInteractionEnabled = NO;
vectorArea = area;
[self addSubview: vectorArea];
}
}
- (void)rotateVector: (UIPanGestureRecognizer *)panRecognizer{
if (touchLocationpoint.x){
PrevioustouchLocationpoint = touchLocationpoint;
}
if ([panRecognizer numberOfTouches] >= 1){
touchLocationpoint = [panRecognizer locationOfTouch:0 inView:self];
}
CGPoint origin;
origin.x=240;
origin.y=160;
CGPoint previousDifference = [self vectorFromPoint:origin toPoint:PrevioustouchLocationpoint];
CGAffineTransform newTransform =CGAffineTransformScale(vectorArea.transform, 1, 1);
CGFloat previousRotation = atan2(previousDifference.y, previousDifference.x);
CGPoint currentDifference = [self vectorFromPoint:origin toPoint:touchLocationpoint];
CGFloat currentRotation = atan2(currentDifference.y, currentDifference.x);
CGFloat newAngle = currentRotation- previousRotation;
newTransform = CGAffineTransformRotate(newTransform, newAngle);
[self animateView:vectorArea toPosition:newTransform];
}
-(CGPoint)vectorFromPoint:(CGPoint)firstPoint toPoint:(CGPoint)secondPoint
{
CGPoint result;
CGFloat x = secondPoint.x-firstPoint.x;
CGFloat y = secondPoint.y-firstPoint.y;
result = CGPointMake(x, y);
return result;
}
-(void)animateView:(UIView *)theView toPosition:(CGAffineTransform) newTransform
{
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.0750];
vectorArea.transform = newTransform;
[UIView commitAnimations];
}
#end
here is attempt to clarify. I am creating the rectangle from a coordinates on a map. Here is the function that creates that rectangle in the main view. Essentially it is the middle of the screen:
overlay is the view created with the above code.
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (!circle){
circle = [MKCircle circleWithCenterCoordinate: userLocation.coordinate radius:100];
[mainMapView addOverlay:circle];
CGPoint centerPoint = [mapView convertCoordinate:userLocation.coordinate toPointToView:self.view];
CGPoint upPoint = CGPointMake(centerPoint.x, centerPoint.y - 100);
overlay = [[OverlaySelectionView alloc] initWithFrame: self.view.frame];
overlay.vectorBounds = CGRectMake(upPoint.x, upPoint.y, 30, 100);
[self.view addSubview: overlay];
}
}
Here is the sketch of what I am trying to achieve:
Introduction
A rotation is always done around (0,0).
What you already know:
To rotate around the center of the rectangle you translate the rect to origin, rotate and translate back.
Now for your question:
to rotate around a center point of a circle, simply move the center of the rectangle such that the circle is at (0,0) then rotate, and move back.
start positioning the rectangle at 12 o clock, with the center line at 12.
1) as explained you always rotate around 0,0, so move the center of the circle to 0,0
CGAffineTransform trans1 = CGAffineTransformTranslation(-circ.x, -circ.y);
2) rotate by angle
CGAffineTransform transRot = CGAffineTransformRotation(angle); // or -angle try out.
3) Move back
CGAffineTransform transBack = CGAffineTransformTranslation(circ.x, circ.y);
Concat these 3 rotation matrices to one combibed matrix, and apply it to the rectangle.
CGAffineTransformation tCombo = CGAffineTransformConcat(trans1, transRot);
tCombo = CGTransformationConcat(tCombo, transback);
Apply
rectangle.transform = tCombo;
You probably should also read the chapter about Transformation matrices in Quartz docu.
This code is written with a text editor only, so expect slighly different function names.
From a couple of previous StackOverFlow questions and this example on github CA360, I've managed to simulate a flipping card with an image on the "front" and text on the back. However, the text on the back of the card is upside down and I only got it to center horizonatally. How can I orient the text properly and center it vertically on my card?
Card front:
Card back (How to orient and center text vertically?):
[Update]
I set the opacity on my top layer to 0.5 then I got the idea to just "pre-flip" the back layer so that when the actual flip happened that it would just reset it back to normal.
My "pre-flip" looks like this:
cardBack.transform = CATransform3DMakeRotation(M_PI, 1.0f, 0.0f, 0.0f); // Pre-flip card
Now I just need to find a built-in way to vertical setting or do it the hard way... Half the distance...font height... plus...
Set up card container with 2 layers
(Reference):
- (void)loadView {
UIView *myView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
myView.backgroundColor = [UIColor whiteColor];
self.view = myView;
cardContainer = [CATransformLayer layer];
cardContainer.frame = CGRectMake(300, 250, 200, 150);
CALayer *cardFront = [CALayer layer];
cardFront.frame = cardContainer.bounds;
cardFront.zPosition = 5; // Higher than the zPosition of the back of the card
cardFront.contents = (id)[UIImage imageNamed:#"Ben"].CGImage;
cardFront.opacity = 0.5;
[cardContainer addSublayer:cardFront];
/*
https://stackoverflow.com/questions/2209734/add-text-to-calayer
*/
CATextLayer *cardBack = [CATextLayer layer];
cardBack.string = #"Hello";
cardBack.frame = cardContainer.bounds;
cardBack.zPosition = 4;
cardBack.backgroundColor = [[UIColor grayColor] CGColor];
cardBack.alignmentMode = kCAAlignmentCenter;
CFTypeRef *font = (CFTypeRef *)CTFontCreateWithName(CFSTR("Times"), 48, NULL);
cardBack.font = font;
[cardContainer addSublayer: cardBack];
[self.view.layer addSublayer:cardContainer];
}
Code borrowed from another SOF question to flip card:
- (void) flipCard {
// [self.flipTimer invalidate];
// if (flipped){
// return;
// }
NSLog(#"Count=%d", count);
id animationsBlock = ^{
// self.backView.alpha = 1.0f;
// self.frontView.alpha = 0.0f;
// [self bringSubviewToFront:self.frontView];
// flipped = YES;
NSLog(#"Flipping...");
CALayer *layer = cardContainer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
if (count == 0) {
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f);
} else { // flip it back to image
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 0.0f, 0.0f, 1.0f, 1.0f);
}
layer.transform = rotationAndPerspectiveTransform;
};
count = (count + 1) % 2;
[UIView animateWithDuration:1.25
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:animationsBlock
completion:nil];
}
Are you aware of the UIView class method:
(void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
You would have two views, the front and the back. both can be in your nib. With this call, you can flip from one view to the other view with almost no effort.
EDIT: I did look at your code. Note that anchorPoint, position, and bounds all play together, but you can still use frame. Its interesting (dare I say fun) to play with changing one and seeing how the others change.
What I ended up doing is comment out all setting of position and frame, and essentially set the text box frame as I would with a UIView (width of large view minus width of small view divided by 2,etc):
cardBackText.frame = CGRectMake((200-100)/2, (150-30)/2, 100,30); // 200, 150
NSLog(#"position=%# anchorPoint=%# bounds=%#", NSStringFromCGPoint(cardBackText.position),NSStringFromCGPoint(cardBackText.anchorPoint),NSStringFromCGRect
The log printed this out:
position={100, 75} anchorPoint={0.5, 0.5} bounds={{0, 0}, {100, 30}}
If I do :
view.transform = CGAffineTransformScale(view.transform, s, s);
My view is scaled, but also its subviews... I don't want the subview to change size. Apparently autoresizesSubviews has no effect on this matter
Suppose that your UIView's stack is arranged as follow:
#interface UIResizableView : UIView <UIGestureRecognizerDelegate> {
UILabel *innerView;
CGPoint startPoint;
}
#end
#implementation UIResizableView
- (id)initWithFrame:(CGRect)aFrame {
self = [super initWithFrame:aFrame];
if (self) {
innerView = [[UILabel alloc] initWithFrame: CGRectMake(10, 10, 115, 50)];
[innerView setFont:[UIFont systemFontWithSize:7]];
[innerView setTextColor:[UIColor blackColor]];
[innerView setBackgroundColor:[UIColor clearColor]];
[innerView setTextAlignment:UITextAlignmentCenter];
[innerView setText:#"hello world!"];
[self innerView];
[innerView release];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)];
[pinchRecognizer setDelegate:self];
[self addGestureRecognizer:pinchRecognizer];
[pinchRecognizer release];
}
return self;
}
#end
Then your scale method would look like the following. Note that the "reframing" of innerView is required to center again the subview in the original position.
-(void)scale:(id)sender {
static float lastScale = 0.0;
if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
lastScale = 1.0;
return;
}
CGFloat scale = 1.0 - (lastScale - [(UIPinchGestureRecognizer*)sender scale]);
CGAffineTransform currentTransform = self.transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
[self setTransform:newTransform];
CGAffineTransform originalSize = CGAffineTransformInvert(self.transform);
CGAffineTransform originalPositionAndSize = CGAffineTransformTranslate(originalSize, self.frame.origin.x, self.frame.origin.y);
[innerView setTransform:originalPositionAndSize];
innerView = CGRectMake(10, 10, 115, 60);
lastScale = [(UIPinchGestureRecognizer*)sender scale];
}
Perhaps you are adding the subview (and using the parent view's frame values to set the size) after you have applied the transform on the parent view. At least that was what I was doing.
I had to compensate for that by applying a inversion
CGRect parentOriginalFrame = CGRectApplyAffineTransform(parentView.frame, CGAffineTransformInvert(parentView.transform));
Now with the parent's original frame, you can set the right frame value for the child.