I have a view, inside that view I have an UIImageview but inside this UIImageview I have 3 more UIImageview, this 3 UIImageview I can drag them and resize them, the problem is that when i resize one of them all of them return to the original position, why is this happening ?
UPDATE:
I use this for dragging the image
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:_photo];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:_photo];}
This for resizing, using an slider
- (IBAction)sizePhoto:(UISlider *)sender {
switch (cambiaSize) {
case 1:
[_imagen1 setTransform:CGAffineTransformMakeScale(sender.value, sender.value)];
break;
case 2:
[_imagen2 setTransform:CGAffineTransformMakeScale(sender.value, sender.value)];
break;
case 3:
[_imagen3 setTransform:CGAffineTransformMakeScale(sender.value, sender.value)];
break;}}
I have also used this for resizing
_imagen1.transform = CGAffineTransformScale(self.view.transform,sender.value, sender.value);
and I also flip horizontally the image using this
_imagen2.transform = CGAffineTransformScale(_imagen2.transform,1.0, 1.0);
I would like to keep the transformation while doing any of this actions...
This could be a consequence of using auto layout. If you directly change the frame of an object, it will revert to the frame defined by the constraints when some other action causes the view to redraw. You either need to turn off auto layout, or change the position of your views by modifying their constraints.
Use CGAffineTransformScale instead of CGAffineTransformMakeScale. 'MakeScale' creates a whole new identity transform with applied scale whereas the former method simply applies a scale to the pre-existing transform.
UIImageView *imageView = [[UIImageView alloc] init];
CGFloat exampleValueA = 90.0f, exampleValueB = 90.0f;
imageView.transform = CGAffineTransformScale(imageView.transform, exampleValueA, exampleValueB);
Related
I am trying to display location of object that is moving by gesture. Everythink works fine untill I want to display its position. Below is code:
- (void)panDetected:(UIPanGestureRecognizer *)panRecognizer
{
CGPoint translation = [panRecognizer translationInView:self.view];
CGPoint imageViewPosition = _iconCamera.center;
imageViewPosition.x += translation.x;
// This line generates error
_labelCameraPosition.text = [NSString stringWithFormat: #"%.1f", _iconCamera.center.x];
self.iconCamera.center = imageViewPosition;
[panRecognizer setTranslation:CGPointZero inView:self.view];
}
When I want to display this value image is shaking but not moving. What should I do?
This answer is based on that you are using auto layout.
in viewDidAppear, set your label and imageView like this.
self.label.translatesAutoresizingMaskIntoConstraints = YES;
self.imageView.translatesAutoresizingMaskIntoConstraints = YES;
since you are changing frame when using auto layout. set translatesAutoresizingMaskIntoConstraints to yes automatically translate its new frame to constraints.
I am developing a board game, and I have 10x10 cells in my board, each cell is taken by an UIImageView, so when player tried to place a piece into the cell, the corresponding cell should be set to an image.
What I want to achieve is when user drag a piece and try to put it onto the board, the piece should fit itself into the UIImageView of the cell. The problem now is I can put the piece anywhere on the board, even between two cells. What can I do to fix it? thanks
----------------Updated:----------------
Since I did everything in storyboard, so there's very few code I can share hare. I have following line of code in .h file:
#property(retain) IBOutletCollection(UIImageView) NSArray *images;
They are an array of UIImageView with length 100, each one of them corresponds to a cell in the board, like the following:
the icons currently on the board are auto generated, the target icon which I want to drag onto the board is located on the upper left of the board ('X' icon). and the following method which handles the drag:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
The problem now is I can place the 'X' anywhere on the board, like the following:
but instead I want to put it to fit into a cell in the board.
You can achieve this by detecting which cell the translation has ended in. For example:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
CGPoint origin = yourGrid.frame.origin; //where yourGrid refers to the main game board
float cellHeight = 30; //enter the corresponding value for cellHeight and cellWidth
float cellWidth = 30; //I've chosen 30 as an arbitrary value.
int translationXIndex = (recognizer.view.center.x + translation.x - origin.x)/cellWidth;
int translationYIndex = (recognizer.view.center.y + translation.y - origin.y)/cellHeight;
//Assuming you count left to right and then up to down
[[images objectAtIndex: (translationXIndex + (10*translationYIndex))] setImage:[UIImage imageNamed:#"YourImage.png"]];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
//You would have to remove the object you just dragged now, since you've changed the image of the corresponding cell
}
How this works: The recogniser looks for the centre of the translation and finds out which cell it lies inside. It then accesses the imageView of that cell and changes the image to the target image.
Hope this helps.
I'm pretty new to auto layout and I'm confused about how to animate views.
I read a lot, and I know you must hold to the constraints, edit it, and wrap the layoutIfNeeded in an UIView animation block.
But when it comes to do it, I'm a little lost. I'd love if someone could explain me how this animation is done for example.
I think it probably uses a UIPanGestureRecognizer to change the constant of the leading space to container constraint, but it probably uses UIDynamics (for the bounce effect at the right ?).
Well, similar behavior could be achieved with UIPanGestureRecognizer + [UIView animateWithDuration:animations:]. Yes, you set leading space constraint and change it according to UIPanGestureRecognizer state. Remember that you need to set final constraints only (define final position of a slider). Intermediate animation positions are calculated for you. For the slider we have default left position and activated middle position.
For view rotation we can use transform property of UIView.
Autolayout constraints in IB:
Setting animation options (UIViewAnimationOptionCurveEaseOut animation curve) could give a feel of bounce effect. UIPanGestureRecognizer code (omit instance variables declaration, because their names are self-explanatory):
- (IBAction)onPan:(UIPanGestureRecognizer*)sender
{
switch (sender.state) {
case UIGestureRecognizerStateBegan:
_startOffset = self.leadingSpace.constant;
_maxOffset = self.slider.superview.frame.size.width
- kHorizontalPadding
- self.slider.frame.size.width;
break;
case UIGestureRecognizerStateChanged: {
CGFloat offset = _startOffset + [sender translationInView:self.slider.superview].x;
offset = MIN(offset, _maxOffset);
self.leadingSpace.constant = offset;
break;
}
case UIGestureRecognizerStateEnded: {
CGFloat offset = _startOffset + [sender translationInView:sender.view.superview].x;
UIColor *bgColor = [UIColor lightGrayColor];
CGFloat rotation = 0;
if (offset < _maxOffset) {
offset = kHorizontalPadding;
}
else {
offset = (_maxOffset + kHorizontalPadding)/2;
bgColor = [UIColor redColor];
rotation = M_PI_2;
}
self.leadingSpace.constant = offset;
[UIView
animateWithDuration:.5
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self.slider layoutIfNeeded];
self.slider.backgroundColor = bgColor;
self.slider.transform = CGAffineTransformMakeRotation(rotation);
} completion:nil];
break;
}
default:
break;
}
}
Animation result with UIViewAnimationOptionCurveLinear (capture simulator):
Animation result with UIViewAnimationOptionCurveEaseOut (capture simulator):
UIDynamics
With UIDynamics things become more complicated. Good starting point is Ray Wenderlich UIKit Dynamics Tutorial.
For bouncing slider we could add following behaviors:
UIGravityBehavior which pulls a slider to start position. We need to change angle property to direct gravity force to the left.
UICollisionBehavior which defines left and right edges of allowed movements. translatesReferenceBoundsIntoBoundary property will be useful if we treat parent view as boundary. Also we need to add extra boundary to stop slider in the middle using addBoundaryWithIdentifier:fromPoint:toPoint (or bezier path).
UIDynamicItemBehavior to change elasticy and possibly resistance properties to configure bounce and acceleration respectively.
Possibly UIPushBehavior in conjunction with recognizer's velocityInView: to specify slider velocity when a user releases a slider
Possibly UISnapBehavior as an alternative to UIGravityBehavior
I've used the following code, shown below, to implement drag & drop in several different places and has always worked well for me in the past.
Now I have a problem with it and have no idea why. It works perfectly so long as I have the device (or simulator) oriented portrait with the button down. But in any of the other three orientations, as the finger dragging the view moves one direction, the "dragged" view moves a different direction.
As shown below I'm logging the value of translation with each move. In the original orientation, as I drag from the middle of the screen toward the lower-left corner, the values for translation are:
-, +
If I rotate left, and do it again:
-, -
Rotate left again:
+, -
Rotate left again:
+, +
I am totally not getting what happens here, particularly since this code seems to work well in other view controllers.
Any suggestions will be appreciated.
- (void) didMakePanGesture:(UIPanGestureRecognizer *)panGesture
{
if (panGesture.state == UIGestureRecognizerStateBegan)
{
[self setDropTargetsCoordinates]; // saves correct drop target & its coordinates
dragViewStartLocation = receptiveClassificationImageView.center; // save center in case we have to snap back
receptiveClassificationImageView.transform = CGAffineTransformMakeScale(0.40f, 0.40f); // make the image smaller for dragging
receptiveClassificationImageView.layer.cornerRadius = 12.0f; // we lose the rounded corners in the scaling; this to fix
}
else if (panGesture.state == UIGestureRecognizerStateChanged)
{
//
// Adjust the location of the dragged view whenever state changes
//
CGPoint translation = [panGesture translationInView:nil];
CGAffineTransform transform = receptiveClassificationImageView.transform;
transform.tx = translation.x;
transform.ty = translation.y;
receptiveClassificationImageView.transform = transform;
NSLog(#"Translation=%f,%f" translation.x, translation.y);
}
else if (panGesture.state == UIGestureRecognizerStateEnded)
{
// do stuff when dropped
}
Just in case someone sees this later, this problem was solved with the following changes to the code above:
if (panGesture.state == UIGestureRecognizerStateBegan)
{
[self setDropTargetsCoordinates]; // saves correct drop target & its coordinates
dragViewStartLocation = receptiveClassificationImageView.center; // save center in case we have to snap back
**receptiveClassificationImageView.center = [panGesture locationInView:receptiveClassificationImageView.superview]; // re-center the view before scaling**
receptiveClassificationImageView.transform = CGAffineTransformMakeScale(0.40f, 0.40f); // make the image smaller for dragging
receptiveClassificationImageView.layer.cornerRadius = 12.0f; // we lose the rounded corners in the scaling; this to fix
}
else if (panGesture.state == UIGestureRecognizerStateChanged)
{
//
// Adjust the location of the dragged view whenever state changes
//
CGPoint translation = [panGesture translationInView:**self.view**];
CGAffineTransform transform = receptiveClassificationImageView.transform;
transform.tx = translation.x;
transform.ty = translation.y;
receptiveClassificationImageView.transform = transform;
}
I have a UIImageView that I rotate around its center:
imageHorizon.layer.anchorPoint = CGPointMake(0.5, 0.5);
imageHorizon.transform = CGAffineTransformRotate(imageHorizon.transform, angleToRotate*(CGFloat)(M_PI/180));
Sometimes I also move this image to the left or right and then rotate again. I would like to keep the rotation center all the time on the same point (which is actually the center of the super view). How can I do that ?
cheers,
self.imgView.layer.anchorPoint = CGPointMake(0.0,1.0);
self.imgView.layer.position = CGPointMake(100,200.0);
CGAffineTransform cgaRotateHr = CGAffineTransformMakeRotation(-(3.141/4));
[self.imgView setTransform:cgaRotateHr];
This is an older question, but the other solutions did not work well for me, so I came up with another solution:
Rotating an image is essentially just a normal rotation with a translation applied, ensuring that the point you want to rotate around is still in the same spot after the rotation. To do this, calculate the position's CGPoint in your image before the rotation, get the position after the rotation, and apply the difference as a translation on the image, "snapping" it into the right position. Here is the code that I've been using:
Keep in mind that the translation should be applied via CGAffineTransform, not moving the .center, because the translation will need to be relative to the rotation, and CGAffineTransformTranslate() takes care of that.
// Note: self is the superview of _imageView
// Get the rotation point
CGPoint rotationPointInSelf = self.center; // or whatever point you want to rotate around
CGPoint rotationPointInImage = [_imageView convertPoint:rotationPointInSelf fromView:self];
// Rotate the image
_imageView.transform = CGAffineTransformRotate(_imageView.transform, angle);
// Get the new location of the rotation point
CGPoint newRotationPointInImage = [_imageView convertPoint:rotationPointInSelf fromView:self];
// Calculate the difference between the point's old position and its new one
CGPoint translation = CGPointMake(rotationPointInImage.x - newRotationPointInImage.x, rotationPointInImage.y - newRotationPointInImage.y);
// Move the image so the point is back in it's old location
_imageView.transform = CGAffineTransformTranslate(_imageView.transform, -translation.x, -translation.y);
You can make the image a subview of another view and then rotate the superview to get that effect. Another approach is to set the anchorPoint property as described in the docs.
I'm using this code to rotate around the point (0,0).
Maybe it help you figure out how to active what you want.
float width = self.view.frame.size.width;
float height = self.view.frame.size.height;
CGRect frame_smallView = CGRectMake(-width, -height, width, height);
UIView *smallView = [[UIView alloc] initWithFrame:frame_smallView];
smallView.backgroundColor = darkGrayColor;
// Select x and y between 0.0-1.0.
// The default is (0.5f,0.5f) that is the center of the layer
// (1.0f,1.0f) is the right bottom corner
smallView.layer.anchorPoint = CGPointMake(1.0f, 1.0f);
// Rotate around this point
smallView.layer.position = CGPointMake(0, 0);
[self.view insertSubview:smallView belowSubview:self.navBar];
[UIView animateWithDuration:1
animations:^{
smallView.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){
[self.navigationController popViewControllerAnimated:NO];
}];