Scaling a UIView along one direction with UIPinchGestureRecognizer - ios

In my app I have a draggable UIView to which I have added a UIPinchGestureRecognizer. Following this tutorial, as default the view is scaled along both x and y directions. Is it possibile to scale along only one direction using one finger? I mean, for example, I tap the UIView both with thumb and index fingers as usual but while I'am holding the thumb I could move only the index finger in one direction and then the UIView should scale along that dimension depending on the direction in which the index moves.
Actually, I have achieved this by adding pins to my UIView like the following:
I was just thinking my app might be of better use using UIPinchGestureRecognizer.
I hope I have explained myself, if you have some hints or tutorial or documentation to link to me I would be very grateful.

In CGAffineTransformScale(view.transform, recognizer.scale, recognizer.scale) update only one axis: X or Y with recognizer.scale and keep the other one 1.0f
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
In order to know which axis you should scale, you need to do some math to see the angle between two fingers of a gesture and based on the angle to decide which axis to scale.
Maybe something like
enum Axis {
case X
case Y
}
func axisFromPoints(p1: CGPoint, _ p2: CGPoint) -> Axis {
let absolutePoint = CGPointMake(p2.x - p1.x, p2.y - p1.y)
let radians = atan2(Double(absolutePoint.x), Double(absolutePoint.y))
let absRad = fabs(radians)
if absRad > M_PI_4 && absRad < 3*M_PI_4 {
return .X
} else {
return .Y
}
}

Related

how to get the actual translation of a uiview

I have an uiview in which I draw my own map with a dimension of say 1000 * 1000.
I am moving and scaling the map with CGAffineTransform....... (Translate, Scale) transformations.
Lets say the upper left coordinate of my map after translation is 20 (x),10(y), because I translatet the map 20 to the left and 10 up.
Is there a way to get the coordinate of the upperleft corner of my map from the view ? (I know I could maintain my own metadata for this and update my metadata after a translation by my self.)
extension CGAffineTransform {
var translation: CGPoint {
return CGPoint(x: tx, y: ty)
}
}

How to attach sprites that collide?

I essentially want the "sprites" to collide when they stick together. However, I don't want the "joint" to be rigid; I essentially want the sprites to be able to move around as long as they are in contact with each other. Imagine two circles connected, and you can move one circle around the other, as long as it remains in contact.
I found this question: How to make one body stick to another moving object in SpriteKit and a lot of other resources that explain how to make sprites stick upon collision, but they all use SKJoints, which are rigid are not really flexible.
I guess another way to phrase it would be to say that I want the sprites to stick, but I want them to be able to "slide" on each other.
Well, I can think of one workaround, but this wouldn't work with non-normal polygons.
Sticking (pun unintended) with your circles example, what if you lock the position of the circle?
let circle1 = center circle
let circle2 = movable circle
Knowing the width of both circles, you can place in the update function that the position should be exactly the distance of:
((circle1.frame.width / 2) + (circle2.frame.width / 2))
If you're up to it, here's some code to help you on your way.
override func update(currentTime: CFTimeInterval) {
{
let distance = hypotf(Float(circle1.position.x - circle2.position.x), Float(circle1.position.y - circle2.position.y))
//calculate circle distances from each other
let radius = ((circle1.frame.width / 2) + (circle2.frame.width / 2))
//distance of circle positions
if distance != radius
{
//if distance is less or more than radius
let pointA = circle1.position
let pointB = circle2.position
let pointC = CGPointMake(pointB.x + 2, pointB.y)
let angle_ab = atan2(pointA.y - pointB.y, pointA.x - pointB.x)
let angle_cb = atan2(pointC.y - pointB.y, pointC.x - pointB.x)
let angle_abc = angle_ab - angle_cb
//get angle of circles from each other using atan2
let vectorx = cos(angle_abc)
let vectory = sin(angle_abc)
//convert angle into vectors
let x = circle1.position.x + radius * vectorx
let y = circle1.position.y + radius * vectory
//get new coordinates from vector, radius and center circle position
circle2.position = CGPointMake(x, y)
//set new position
}
}
Well you need to write code to make sure the movable circle, is well movable.
But, this should work.
I haven't tested this yet though, and I haven't even learned geometry let alone trig in school yet.
If I'm reading your question as you intended it, you can still use joints- just create actions with Inverse Kinematic constraints that allow rotation and translation around the contacting circles' joint.
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKAction_Ref/index.html#//apple_ref/doc/uid/TP40013017-CH1-SW72

Check if node is visible on the screen

I currently have a large map that goes off the screen, because of this its coordinate system is very different from my other nodes. This has led me to a problem, because I'm needing to generate a random CGPoint within the bounds of this map, and then if that point is frame/on-screen I place a visible node there. However the check on wether or not the node is on screen continuously fails.
I'm checking if the node is in frame with the following code: CGRectContainsPoint(self.frame, values) (With values being the random CGPoint I generated). Now this is where my problem comes in, the coordinate system of the frame is completely different from the coordinate system of the map.
For example, in the picture below the ball with the arrows pointing to it is at coordinates (479, 402) in the scene's coordinates, but they are actually at (9691, 9753) in the map's coordinates.
I determined the coordinates using the touchesBegan event for those who are wondering. So basically, how do I convert that map coordinate system to one that will work for the frame?
Because as seen below, the dot is obviously in the frame however the CGRectContainsPoint always fails. I've tried doing scene.convertPoint(position, fromNode: map) but it didn't work.
Edit: (to clarify some things)
My view hierarchy looks something like this:
The map node goes off screen and is about 10,000x10,000 for size. (I have it as a scrolling type map). The origin (Or 0,0) for this node is in the bottom left corner, where the map starts, meaning the origin is offscreen. In the picture above, I'm near the top right part of the map. I'm generating a random CGPoint with the following code (Passing it the maps frame) as an extension to CGPoint:
static func randPoint(within: CGRect) -> CGPoint {
var point = within.origin
point.x += CGFloat(arc4random() % UInt32(within.size.width))
point.y += CGFloat(arc4random() % UInt32(within.size.height))
return point;
}
I then have the following code (Called in didMoveToView, note that I'm applying this to nodes I'm generating - I just left that code out). Where values is the random position.
let values = CGPoint.randPoint(map.totalFrame)
if !CGRectContainsPoint(self.frame, convertPointToView(scene!.convertPoint(values, fromNode: map))) {
color = UIColor.clearColor()
}
To make nodes that are off screen be invisible. (Since the user can scroll the map background). This always passes as true, making all nodes invisible, even though nodes are indeed within the frame (As seen in the picture above, where I commented out the clear color code).
If I understand your question correctly, you have an SKScene that contains an SKSpriteNode that is larger than the scene's view, and that you are randomly generating coordinates within that sprite's coordinate system that you want to map to the view.
You're on the right track with SKNode's convertPoint(_:fromNode:) (where your scene is the SKNode and your map is the fromNode). That should get you from the generated map coordinate to the scene coordinate. Next, convert that coordinate to the view's coordinate system using your scene's convertPointToView(_:). The point will be out of bounds if it is not in view.
Using a worldNode which includes a playerNode and having the camera center on this node, you can check on/off with this code:
float left = player.position.x - 700;
float right = player.position.x + 700;
float up = player.position.y + 450;
float down = player.position.y - 450;
if((object.position.x > left) && (object.position.x < right) && (object.position.y > down) && (object.position.y < up)) {
if((object.parent == nil) && (object.dead == false)) {
[worldNode addChild:object];
}
} else {
if(object.parent != nil) {
[object removeFromParent];
}
}
The numbers I used above are static. You can also make them dynamic:
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
Diving the screenWidth by 2 for left and right. Same for screenHeight.

Character not moving in the correct direction in Scenekit

I am making a fist person camera in Scenekit and the character is not moving relative to the rotation. Instead it is moving relative to the world axis. I want my game to be so when the player swipes forward, the character moves forward on its own x axis. Not along the x axis of the world. I tried to compensate of this with sine and cosine but it did not work. Here is my code:
func lookGestureRecognized(gesture: UIPanGestureRecognizer) {
let velocity = gesture.velocityInView(sceneView)
let rotationAngle = heroNode.presentationNode().rotation.w * heroNode.presentationNode().rotation.y
var impulse = SCNVector3Make(Float(velocity.x)/50, 0, Float(velocity.y)/50)
impulse = SCNVector3(x: impulse.x * cos(rotationAngle), y: 0, z: impulse.z * sin(rotationAngle))
heroNode.physicsBody?.applyForce(impulse, impulse: true)
}
This is still making the character move in the wrong direction of the swipe. Does anyone know how to make the character move relative to its rotation?
Thanks, please ask for clarification if needed
the rotation property is made of a rotation axis and a rotation angle. If you want to retrieve the rotation along the x axis you can use the eulerAngles property.

Detect if UIbuttons overlap

I have two UIButtons on my view, and I am trying to detect if the two overlap, in oder to do:
if (overlap)
move the second button
I have tried this:
if (BluetoothDeviceButton.X1 < btn.X2 && BluetoothDeviceButton.X2 > btn.X1 &&
BluetoothDeviceButton.Y1 < btn.Y2 && BluetoothDeviceButton.Y2 > btn.Y1){
}
I can't really get what I should put instead of X1, X2, etc. And I don't really know if this method is going to work at all.
CGRectIntersectsRect(CGRect rect1, CGRect rect2) will tell you if their frames overlap.
if (CGRectIntersectsRect(btn.frame, BluetoothDeviceButton.frame)) {
...
}
You need to use BluetoothDeviceButton.frame.origin.x or BluetoothDeviceButton.center.x.
Firstly make sure they are in the same view. If not, then get their frames using convertRectToView, or converRectFromView. Then use the CGRectContainsPoint of the CGGeometry class and check if any corner of one button lies in the frame of the other button.
PS. your corners will be:
CGFloat x1 = button1.frame.origin.x;
CGFloat y1 = button1.frame.origin.y;
CGFloat x2 = button1.frame.origin.x + button1.frame.size.width;
CGFloat y2 = button1.frame.origin.y + button1.frame.size.height;
Your corners will be:
CGPoint topLeft = CGPointMake(x1,y1);
CGPoint topRight = CGPointMake(x2,y1);
CGPoint bottomLeft = CGPointMake(x1,y2);
CGPoint bottomRight = CGPointMake(x2,y2);
This is just one possible solution. This is just to help understand geometry.
But the simplest solution would be using CGRectIntersectsRect (button1.frame, button2.frame);
Use BluetoothDeviceButton.frame.origin.x. Frame property contain also size.

Resources