I'm using Xcode 5.1 and I have several UILabels that I need to swap positions. I have Auto Layout turned off. I need these labels to swap position during a swipe gesture. I'm already swapping the position of two UIContainer objects, you'll see that in the code, and I need the labels to travel along with the container. Here's my code:
- (IBAction)handleSwipe:(UISwipeGestureRecognizer *)recognizer
{
//Get the center of each view container
CGPoint targetHomeCenter = _vistingTeamContainer.center;
CGPoint targetVisitorCenter = _homeTeamContainer.center;
CGPoint targetHomeNameCenter = _visitingTeamName.center;
CGPoint targetVisitorNameCenter = _homeTeamName.center;
//Get the center of each past score label
CGPoint targetHomeGame1 = self.visitGame1.center;
CGPoint targetHomeGame2 = self.visitGame2.center;
CGPoint targetHomeGame3 = self.visitGame3.center;
CGPoint targetVisitGame1 = self.homeGame1.center;
CGPoint targetVisitGame2 = self.homeGame2.center;
CGPoint targetVisitGame3 = self.homeGame3.center;
//Create the animation and swap positions of the controllers
[UIView animateWithDuration:0.7f
delay:0.0f
usingSpringWithDamping:0.7f
initialSpringVelocity:0.5f
options:UIViewAnimationOptionCurveEaseInOut
animations:^(){
//Move the containers
_homeTeamContainer.center = targetHomeCenter;
_homeTeamName.center = targetHomeNameCenter;
_vistingTeamContainer.center = targetVisitorCenter;
_visitingTeamName.center = targetVisitorNameCenter;
//Now move the past scores
self.homeGame1.center = targetVisitGame1;
self.homeGame2.center = targetVisitGame2;
self.homeGame3.center = targetVisitGame3;
self.visitGame1.center = targetHomeGame1;
self.visitGame2.center = targetHomeGame2;
self.visitGame3.center = targetHomeGame3;
}
completion:NULL];
};
The UIContainers move just fine, as expected. The UILabels don't move at all.
I have also tried CGPoint targetHomeGame1 = _visitGame1.center but that doesn't do anything either. Any help would be appreciated. Thanks!
CGPoint targetVisitGame1 = _homeGame1.center;
self.homeGame1.center = targetVisitGame1;
Why would this want to move? There is no change in point's coordinates.
Related
I'm trying to implement drag controls with boundary limitation on UIButton.
And I wrote following code for that.
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
if (btn.frame.origin.x >= buttonOffPosition && btn.frame.origin.x <= buttonOnPosition) {
btn.center=CGPointMake(btn.center.x+dX, btn.center.y);
NSLog(#"buttonOffPos: %f", buttonOffPosition);
NSLog(#"btn.center.x+dX: %f", btn.center.x+dX);
NSLog(#"buttonOnPos: %f", buttonOnPosition);
}
}
This works almost properly. But only when the button is dragged very fast, it exceeds the limitation buttonOffPosition and buttonOnPosition.
This is the problem I want to resolve. Is there a good way to solve this problem?
Your thoughts and help will be hugely appreciated.
If you want the code to limit your button's location to between your two values you need to check what the final resting point of your button will be after this touch event is processed, not check where it currently is. If it's currently in bounds to be moved, then you move it by say 1000, it will no longer be in bounds at the end but you allow it to go there because you didn't check the end point.
You can do this several ways. The simplest one that comes to my mind is:
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
//Get the new origin after this motion
float newXOrigin = btn.frame.origin.x + dX;
//Make sure it's within your two bounds
newXOrigin = MIN(newXOrigin,buttonOnPosition);
newXOrigin = MAX(newXOrigin,buttonOffPosition);
//Now get the new dX value staying in bounds
dX = newXOrigin - btn.frame.origin.x;
btn.center=CGPointMake(btn.center.x+dX, btn.center.y);
}
This method brings up a problem with your finger no longer being inside the button as you are dragging it from one direction to the other, but I will leave that problem to your next question.
EDIT:
Here is how I would make it more readable. This is only my opinion and has no bearing on the way the code works. Outside of just modifying your code, I would create a cached starting point for the button object and do all motion events from that. That way if your button stops moving even though your finger continues, as your finger comes back the button stays with your finger. This current solution, as soon as your finger changes direction the button will start moving again even though your finger is far off the button now. But to do that would be a large code change for you.
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
//This code can go awry if there is more than one finger on the screen, careful
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
//Get the original position of the button
CGRect buttonFrame = btn.frame;
buttonFrame.origin.x += dX;
//Make sure it's within your two bounds
buttonFrame.origin.x = MIN(buttonFrame.origin.x,buttonOnPosition);
buttonFrame.origin.x = MAX(buttonFrame.origin.x,buttonOffPosition);
//Set the button's new frame if we need to
if(buttonFrame.origin.x != btn.frame.origin.x)
btn.frame = buttonFrame
}
I'm following Ray Wenderlich's 'iOS Games by Tutorials' & I got everything in my world setup & working: The entire game is in Landscape mode & there's one SKNode called _worldNode & everything, except _uiNode (in-game UI), is added to it. Player character walks to a touched location & _worldNode moves under him like a treadmill. However, like all functionality (or as they call it: "juice") addicts I wanted to add zoom in/out functionality through UIPinchGestureRecognizer by scaling _worldNode, which I did. But now every time I zoom in, the "camera" moves to the bottom left. Zooming out moves the view to the top right of the screen. It's a mess. I need the view to stay centered on the player character & I've tried everything I could come up with & find online. The closest thing I came to was using the technique from SKNode scale from the touched point but I still get the bloody bottom left/top right mess. I realized this mess happens only when I update the camera/view (it's really _worldNode.position). Therefore, 'didSimulatePhysics' or 'didFinishUpdate' methods don't help. In fact, even a one time button that slightly moves/updates the camera view (_worldNode.position) still gives me the bottom left/top right problem. Here is my code. I hope someone can take a look & tell me what to modify to get things working.
#interface GameScene () <SKPhysicsContactDelegate, UIGestureRecognizerDelegate>
{
UIPinchGestureRecognizer *pinchGestureRecognizer;
}
//Properties of my GameScene.
#property SKNode *worldNode;
#property etc. etc.
//Called by -(id)initWithSize:(CGSize)size method & creates the in-game world.
-(void)createWorld
{
[_worldNode addChild:_backgroundLayer];
[self addChild:_worldNode];
self.anchorPoint = CGPointMake(0.5, 0.5); //RW tutorial did it this way.
_worldNode.position = CGPointMake(-_backgroundLayer.layerSize.width/2, -_backgroundLayer.layerSize.height/2); //Center.
//Then I add every node to _worldNode from this point on.
}
//Neccessary for gesture recognizers.
-(void)didMoveToView:(SKView *)view
{
pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleZoomFrom:)];
[view addGestureRecognizer:pinchGestureRecognizer];
}
//"Camera" follows player character. 'didSimulatePhysics' doesn't help either.
-(void)didFinishUpdate
{
//IF '_pinchingScreen' == YES then screen pinching is in progress. Thus, camera position update will seize for the duration of pinching.
if (!_pinchingScreen)
{
_worldNode.position = [self pointToCenterViewOn:_player.position];
}
}
//Method that is called by my UIPinchGestureRecognizer. Answer from: https://stackoverflow.com/questions/21900614/sknode-scale-from-the-touched-point?lq=1
-(void)handleZoomFrom:(UIPinchGestureRecognizer*)recognizer
{
CGPoint anchorPoint = [recognizer locationInView:recognizer.view];
anchorPoint = [self convertPointFromView:anchorPoint];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
// No code needed for zooming...
_player.movementMode = 2; //Stop character from moving from touches.
_pinchingScreen = YES; //Notifies 'didFinishUpdate' method that pinching began & camera position update should stop for now.
}
else if (recognizer.state == UIGestureRecognizerStateChanged)
{
//Technique from the above Stack Overflow link - Commented out.
// CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
//
// [_worldNode setScale:(_worldNode.xScale * recognizer.scale)];
//
// CGPoint mySkNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
// CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, mySkNodeAnchorPointInScene);
//
// _worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//
// recognizer.scale = 1.0;
//Modified scale: 2.0
if(recognizer.scale > _previousWorldScale)
{
_previousWorldScale = recognizer.scale;
CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
[_worldNode setScale:2.0];
CGPoint worldNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, worldNodeAnchorPointInScene);
_worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//[_worldNode runAction:[SKAction scaleTo:2.0 duration:0]]; //This works too.
}
//Original scale: 1.0
if(recognizer.scale < _previousWorldScale)
{
_previousWorldScale = recognizer.scale;
CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
[_worldNode setScale:1.0];
CGPoint worldNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, worldNodeAnchorPointInScene);
_worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//[_worldNode runAction:[SKAction scaleTo:1.0 duration:0]]; //This works too.
}
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
// No code needed here for zooming...
_pinchingScreen = NO; //Notifies 'didFinishUpdate' method that pinching has stopped & camera position update should resume.
_player.movementMode = 0; //Resume character movement.
}
}
So could anyone please tell me, by looking at the above code, why the camera/view shifts to the bottom left upon zooming in? I've sat several days on this problem & I still can't figure it out.
Thanks to JKallio, who wrote detailed code in his answer to Zoom and Scroll SKNode in SpriteKit, I've been able to find a piece of code that solves the problem. There's a method called 'centerOnNode' that is small, elegant & solves my problem perfectly. Here it is for anyone that just needs that:
-(void) centerOnNode:(SKNode*)node
{
CGPoint posInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - posInScene.x, node.parent.position.y - posInScene.y);
}
Then you call that method inside your 'didSimulatePhysics' or inside 'didFinishUpdate' like so:
//"Camera" follows player character.
-(void)didFinishUpdate
{
//IF '_pinchingScreen' == YES then screen pinching is in progress. Thus, camera position update will seize for the duration of pinching.
if (!_pinchingScreen)
{
if (_previousWorldScale > 1.0) //If _worldNode scale is greater than 1.0
{
[self centerOnNode:_player]; //THIS IS THE METHOD THAT SOLVES THE PROBLEM!
}
else if (_previousWorldScale == 1.0) //Standard _worldNode scale: 1.0
{
_worldNode.position = [self pointToCenterViewOn:_player.position];
}
}
}
P.S. Just remember the question wasn't HOW to zoom in. But how to fix the issue with the camera once the world is ALREADY zoomed in.
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.
In Xcode, how would I make an image appear in a certain place on the iPhone screen by touching the place where I would want it to appear? The method that I'm currently trying to use is by getting the coordinates of the point and then trying to get a png file to appear at that point. Here is what I have so far for code:
- (void) touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
startPoint = [theTouch locationInView:self.view];
CGFloat x = startPoint.x;
CGFloat y = startPoint.y;
But now that I have the points, I'm not sure how to get an image to appear there or if there might be a more efficient method. Any ideas?
You can use [-UIImage drawAtPoint:].
After getting x,y coordinates of a touch event, set center point of your UIImageView to it.
UIImageView *myImage = ...
CGPoint myPoint = CGPointMake(x_touch, y_touch);
[myImage setCenter:myPoint];
you can also animate this change by using animate block on the image view like so:
nsTimerInterval myAnimationDuration = 1.0;
[UIView animateWithDuration:myAnimationDuration animations:^{
[myImage setCenter:myPoint];
}
completion:^(BOOL finished){
//handle completion tasks
}];
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];
}];