Check view is completely covered by other views - ios

I have a UIView that I have adjusted its layer to make it appear as a circle. (view.layer.cornerRadius = view.frame.size.height/2)
There is also n other smaller circles created this way.
The aim of the user is to completely cover the first circle with the smaller circles by dragging and dropping them over the circle.
How can I check that the large circle has been completely covered?
I have looked at this question Determine whether UIView is covered by other views? but I am unsure of how to obtain the UIBezierPath of the views layer.
Any help is appreciated, Thanks!

You can construct accumulatedPath with this answer Determine whether UIView is covered by other views? using this method:
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect
Then you can enumerate some points on you view circle and ask path about:
- containsPoint:
Example code:
- (BOOL)isCircleView:(UIView *)view coveredWith:(UIBezierPath *)path
{
if (![path containsPoint:view.center])
return NO;
CGFloat r = CGRectGetWidth(view.bounds)/2;
for (CGFloat angle = 0; angle < 360; angle += 0.5) {
CGFloat alpha = angle/180*M_PI;
CGFloat x = view.center.x + r*cos(alpha);
CGFloat y = view.center.y + r*sin(alpha);
if (![path containsPoint:CGPointMake(x,y)])
return NO;
}
return YES;
}
This algorithm uses 720 points on your circle view bounds and center point. More points you'll use – more accurate result you will get.
But there is possible situation when border line is hidden and center is hidden but some part are visible. So we can add one more loop to this method before return YES;:
for (CGFloat x = view.center.x - r; x < view.center.x + r; x += 4)
for (CGFloat y = view.center.y - r; y < view.center.y + r; y += 4)
{
// Comparing distance to center with radius
if (pow(x-view.center.x,2)+pow(y-view.center.y,2) > pow(r,2))
continue;
if (![path containsPoint:CGPointMake(x,y)])
return NO;
}
You can also configure grid step for more accurate result.
UPDATE:
Here is more common method to check if one UIBezierPath is fully overlapped with another UIBezierPath. Third argument will help you to get more accurate result, try to use values like 10, 100.
- (BOOL)isPath:(UIBezierPath *)path overlappedBy:(UIBezierPath *)superPath granularity:(NSInteger)granularity
{
for (NSInteger i = 0; i < granularity; i++)
for (NSInteger j = 0; j < granularity; j++)
{
CGFloat x = CGRectGetMinX(path.bounds) + i*CGRectGetWidth(path.bounds)/granularity;
CGFloat y = CGRectGetMinY(path.bounds) + j*CGRectGetHeight(path.bounds)/granularity;
if (![path containsPoint:CGPointMake(x,y)])
continue;
if (![superPath containsPoint:CGPointMake(x,y)])
return NO;
}
return YES;
}
For circles case I recommend to use first solution, for random shapes – second solution.

To get the UIBezierPath of the views you can use this method:
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect
using the views frame as the rect. As your views have a square shape, you'll get an UIBezierPath that corresponds to your circle.
You then combine all your path into one that you compare with the original circle path.

There would be another possible solution to get bezier path of a view. You should create a UIView class. Make this class a parent of all your circle view. Also declare a UIBezierPath as a property. When you create any circle view assign bezier path.
Next time when you touch your view, you can get its path by accessing its property.

Related

Restrict Touch on ColorPicker UIView - iOS

I am using third party Color picker wheel (ISColorWheel- https://github.com/justinmeiners/ios-color-wheel) to pick a color and display it on screen. I need to restrict selecting blue color if particular button is enabled.
When i see the color picker library class, they have implemented following code to restrict the Knob view to move around the color picker.
- (void)setTouchPoint:(CGPoint)point
{
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
CGPoint center = CGPointMake(width / 2.0, height / 2.0);
// Check if the touch is outside the wheel
if (ISColorWheel_PointDistance(center, point) < _radius)
{
//NSLog(#"Distance is %f and Radius is %f",ISColorWheel_PointDistance(center, point),_radius);
_touchPoint = point;
}
else
{
// If so we need to create a drection vector and calculate the constrained point
CGPoint vec = CGPointMake(point.x - center.x, point.y - center.y);
float extents = sqrtf((vec.x * vec.x) + (vec.y * vec.y));
vec.x /= extents;
vec.y /= extents;
_touchPoint = CGPointMake(center.x + vec.x * _radius, center.y + vec.y * _radius);
NSLog(#"Touch Point is %f %f",_touchPoint.x,_touchPoint.y);
}
[self updateKnob];
}
The above code restrict the user to move knobView away from the circle. In my case i need to restrict the user not to select Blue color of ColorPicker. How can i implement it. How to find the trajectory of Blue color.
You should define a triangle that defines the color blue as you see it (How much green dose it contain in one side how much purple on the other) then look for you Point inside that triangle. One way to do it is here: https://stackoverflow.com/a/9755252/1870192

increment rotation while spacing and loading sprites around parentnode

Progress so far:
So what I have at the moment is this:
(the green point represents the parent "BlankNode, adding children then rotating them around that node,
Im a bit stick how to get it work properly, for some reason they dont sit next to eachother but opposite (as showen in http://i.stack.imgur.com/w7QvS.png)
inGameLevel
myArc = [[Arcs alloc]initWithArcCount:myAmmountOfSprites];
[self addChild:myArc];
My wish is for the sprite.rotation to be slightly offset from the next loaded...here they are split...
(The diagram belows showing the arc shape I would like to load the sprites in)
**With one stick loaded, maybe its easier to spot the mistake
(if I load a second sprite it loads directly opposite to the previous and not at the expected angle incremented
In this version I have just loaded the stick and blanknode, positioned it using anchor points, Im confused how the rotation works... **
SKSpriteNode *blank = [[SKSpriteNode alloc]
///like the otherone
blank.zRotation=0;
blank.anchorPoint = CGPointMake(0.5, 0.5);
[self addChild:blank];
//set to 0 value so I can see what its natural state is (it is vertical and above the parent node)
//but this value will be incremented each time a new sprite is added
int rotationAmount = 0;
Rotation = Rotation-rotationAmount; //will increment
objectPic = [SKSpriteNode spriteNode....as normal
//use blank nodes anchorpoint
objectPic.anchorPoint = blank.anchorPoint;
//Rotation
objectPic.zRotation = Rotation;
float moveUp_donut = 0.3;
//"moveUp_donut" moving this value up moves the stick up
//and outward from the center
objectPic.anchorPoint =
CGPointMake(0.0,-moveUp_donut); //(0.0,-moveOutward);
[blank addChild:objectPic];
}
}
I have made an xcode project available for anyone interested to have a look at the problem, hopefully you can explain how to get the rotation working correctly.
at the moment it is just loading one sprite, so you might need to play with the setting,
myArc = [[Arcs alloc]initWithArcCount:addLotsOfSticks];
//and play with the rotation ammount
int rotationAmount = 3;
http://www.filedropper.com/rotationtest
Solution Found! see below:
🌸
A huge thanks to WangYudong for giving such a great answer!
I made a sample project and hope it can help. The algorithm is not base on your project, so make some change to fit your need.
Firstly, add a blank node to the middle of the scene:
self.blank = [[SKSpriteNode alloc] initWithColor:[SKColor greenColor]size:CGSizeMake(20, 20)];
self.blank.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:self.blank];
Then, create the stick:
- (SKSpriteNode *)newStick
{
SKSpriteNode *stick = [[SKSpriteNode alloc] initWithColor:[SKColor redColor]size:CGSizeMake(5, 100)];
return stick;
}
And given the amount of sticks, the radius (of the inner circle), the starting radian and ending radian, add a method:
- (void)loadStickArcWithStickAmount:(NSUInteger)amount radius:(CGFloat)radius startRadians:(CGFloat)startRad endRadians:(CGFloat)endRad
{
for (NSUInteger index = 0; index < amount; index++) {
SKSpriteNode *stick = [self newStick];
CGFloat halfStickLength = stick.size.height / 2;
CGFloat rotateRad = startRad + (endRad - startRad) / (amount - 1) * index;
stick.zRotation = M_PI_2 + rotateRad;
stick.position = CGPointMake((radius + halfStickLength) * cos(rotateRad),
(radius + halfStickLength) * sin(rotateRad));
[self.blank addChild:stick];
}
}
Some hints:
rotateRad divides radians of endRad - startRad.
M_PI_2 is an offset of zRotation.
Trigonometric maths calculates the position of sticks.
Both anchor points of blank node and stick remain default (0.5, 0.5).
Use the method:
[self loadStickArcWithStickAmount:27 radius:50.0 startRadians:M_PI endRadians:2*M_PI];
to achieve the following result:

Round frames intersecting

Im trying to find out how to tell if my 2 round frames are intersecting each other. since they are round i cant use cgrectintersectsrect and am not sure how to go about this. is there a cgframeintersectsframe or something along those lines?
for my round uiimageviews i did
circle1 = [[uiimageview alloc] initwithframe:cgrectmake (100,100,50,50);
circle1.layer.cornerradius = 25;
circle1.clipstobounds = yes;
[self.view addsubview:circle1];
the other circle is basically like that too except with a different x and y origin
I also alreday imported quartzcore
Just calculate the distance between the centers of your circles and check if it's smaller than the radius:
float distanceBetweenCenters = sqrt(pow(circle1.center.x - circle2.center.x, 2) +
pow(circle1.center.y - circle2.center.y, 2));
BOOL isIntersecting = distanceBetweenCenters <= 2 * radius;
This will tell you whether the circles are intersecting or touching each other. Replace the <= with < to exclude 'touching'.

How do I detect how close UIView view is to the other UIView?

Is there a magic method that accepts two views and returns how close they are to one another (perhaps the x and y distances)? Or is this something that must be done manually?
To get the magical method you're looking for you should write a category on UIView:
// UIView+distance.h
#import <UIKit/UIKit.h>
#interface UIView (distance)
-(double)distanceToView:(UIView *)view;
#end
// UIView+distance.m
#import "UIView+distance.h"
#implementation UIView (distance)
-(double)distanceToView:(UIView *)view
{
return sqrt(pow(view.center.x - self.center.x, 2) + pow(view.center.y - self.center.y, 2));
}
#end
You can call this function from a view like:
double distance = [self distanceToView:otherView];
Or between two views like:
double distance = [view1 distanceToView:view2];
You could also write categories for distance to closest edge, etc. The above formula is just the distance between two points, I used the center of each view. For more information on categories see the Apple docs.
Manually. As long as there is no transform applied to the views, it shouldn't be that hard to write some code that would calculate the distance between the 2 view's frame rectangles.
Thinking out loud here:
If you can't draw a vertical and horizontal line in the space between the 2 views frame rectangles, the distance will be the x distance or y distance between the nearest sides.
If you can draw both a horizontal line and a vertical line between the views (they don't overlap in either the x dimension or the y dimension) then the distance between the 2 views will be the pythagorean distance between their nearest corners.
Must be done manually.
- (double)distance:(UIView*)view1 view2:(UIView*)view2
{
double dx = CGRectGetMinX(view2) - CGRectGetMinX(view1);
double dy = CGRectGetMinY(view2) - CGRectGetMinY(view1);
return sqrt(dx * dx + dy * dy);
or
return sqrt(pow(dx, 2) + pow(dy, 2));
}
CGRectGetMinX() | CGRectGetMaxX() and CGRectGetMinY() | CGRectGetMaxY() can help you a lot.

Is it possible to rotate a Node around an arbitrary point in SpriteKit?

Is there a way to rotate a Node in SpriteKit around an arbitrary point?
I now I can manipulate the anchorPoint of my Node, but that is not sufficient if the rotation point I want to use lies outside of the Node.
What is the best way to achieve this kind of rotation in SpriteKit?
Since you're asking for the best way, here's one that works well (best is subjective):
Create an SKNode and set its position to the center of rotation. Add the node that should rotate around that center as child to the center node. Set the child node's position to the desired offset (ie radius, say x + 100). Change the rotation property of the center node to make the child node(s) rotate around the center point. The same works for cocos2d btw.
I was also trying to solve this problem a few weeks back, and did not implement the anchor points solution because I did not want to have to worry about removing the anchor point when lets say the object collides with another node and should leave its orbit and bounce away.
Instead, I came up with two solutions, both of which work if tweaked. The first took a long time to perfect, and is still not perfect. It involves calculating a certain number of points around a center position offset by a set radius, and then if a certain object comes in a certain distance of the center point, it will continually use physics to send the object on a trajectory path along the "circumference" of the circle, points that it calculated (see above).
There are two ways of calculating points with a radius
The first uses the pythagorean theorem, and the second ultimately uses trigonometry proper.
In the first, you increment a for loop by a certain amount, while it is less that 361 (degree), and for each iteration of the loop, calculate using sine and cosine a point with that angle at a certain radius from the center point.
The second uses the pythagorean theorem, and its code is below:
After you calculate points, you should create a scheduled selector [<object> scheduled selector...]; or a timer in your didMoveToView, or use a fixed update method, in addition to an instance variable called int which will hold the index of the next location to which your object will move. Every time the timer method is called, it will move the object to the next point in your calculate points array using your own or the below code labeled physicsMovement; You can play around with the physics values, and even the frequency of the ttimer for different movement effects. Just make sure that you are getting the index right.
Also, for more realism, I used a method which calculates the closest point in the array of calculated point to the object, which is called only once the collision begins. It is also below labeled nearestPointGoTo.
If you need any more help, just say so in the comments.
Keep Hacking!
I used the second, and here is the source code for it:
The code itself didn't go through
Second point calculation option
+(NSArray *)calculatePoints:(CGPoint)point withRadius:(CGFloat)radius numberOfPoints: (int)numberOfPoints{ //or sprite kit equivalent thereof
// [drawNode clear];
NSMutableArray *points = [[NSMutableArray alloc]init];
for (int j = 1; j < 5; j++) {
float currentDistance;
float myRadius = radius;
float xAdd;
float yAdd;
int xMultiplier;
int yMultiplier;
CCColor *color = [[CCColor alloc]init]; //Will be used later to draw the position of the node, for debugging only
for (int i = 0; i < numberOfPoints; i += 1){
//You also have to change the if (indextogoto == <value>) in the moveGumliMethod;
float opposite = sqrtf( powf(myRadius, 2) - powf(currentDistance, 2) );
currentDistance = i;
switch (j) {
case 1:
xMultiplier = 1;
yMultiplier = 1;
xAdd = currentDistance;
yAdd = opposite;
color = [CCColor blueColor];
break;
case 2:
xMultiplier = 1;
yMultiplier = -1;
xAdd = opposite;
yAdd = currentDistance;
color = [CCColor orangeColor];
break;
case 3:
xMultiplier = -1;
yMultiplier = -1;
xAdd = currentDistance;
yAdd = opposite;
color = [CCColor redColor];
break;
case 4:
xMultiplier = -1;
yMultiplier = 1;
xAdd = opposite;
yAdd = currentDistance;
color = [CCColor purpleColor];
break;
default:
break;
}
int x = (CGFloat)(point.x + xAdd * xMultiplier); //or sprite kit equivalent thereof
int y = (CGFloat)(point.y + yAdd * yMultiplier); //or sprite kit equivalent thereof
CGPoint newPoint = CGPointMake((CGFloat)x,(CGFloat)y); //or sprite kit equivalent thereof
NSValue *pointWrapper = [NSValue valueWithCGPoint:newPoint]; //or sprite kit equivalent thereof
NSLog(#"Point is %#",pointWrapper);
[points addObject:pointWrapper];
}
}
return points;
}
Calculating Nearest Point To Object
-(CGPoint)calculateNearestGumliPoint:(CGPoint)search point { // MY Character is named Gumli
float closestDist = 2000;
CGPoint closestPt = ccp(0,0);
for (NSValue *point in points) {
CGPoint cgPoint = [point CGPointValue];
float dist = sqrt(pow( (cgPoint.x - searchpoint.x), 2) + pow( (cgPoint.y - searchpoint.y), 2));
if (dist < closestDist) {
closestDist = dist;
closestPt = cgPoint;
}
}
return closestPt;
}
I think the best way to make this work is through two SKNode and joint them with SKPhysicsJointPin (Look at the pin example below)
I tried to hang a door sign (SKSpriteNode) on my door(`SkScene), and would like to rotate around on the hanging spot when someone touch it
What I did is making a 1x1 SKNode with a HUGH mass and disabled it's gravity effects.
var doorSignAnchor = SKSpriteNode(color: myUIColor, size: CGSize(width: 1, height: 1))
doorSignAnchor.physicsBody = SKPhysicsBody(rectangleOf: doorSignAnchor.frame.size)
doorSignAnchor.physicsBody!.affectedByGravity = false // MAGIC PART
doorSignAnchor.physicsBody!.mass = 9999999999 // MAGIC PART
var doorSignNode = SKSpriteNode(imageNamed:"doorSign")
doorSignNode.physicsBody = SKPhysicsBody(rectangleOf: doorSignNode.frame.size)
and created a SKPhysicsJointPin to connect them all
let joint = SKPhysicsJointPin.joint(
withBodyA: doorSignAnchor.physicsBody!,
bodyB: doorSignNode.physicsBody!,
anchor: doorSignAnchor.position)
mySkScene.physicsWorld.add(joint)
So it will move like actual door sign, rotate around an arbitrary point (doorSignAnchor)
Reference:
Official document about Sumulating Physics
How to Make Hanging Chains With SpriteKit Physis Joints

Resources