Understanding CGRectDivide() - ios

I am trying to experiment with UIViews on screen and using pan gestures. So I got some open source code from another project that I am looking at - and trying to learn a few things from it.
-(BOOL)isPointContainedWithinBezelRect:(CGPoint)point {
CGRect leftBezelRect;
CGRect tempRect;
CGFloat bezelWidth = 20;
CGRectDivide(self.view.bounds, &leftBezelRect, &tempRect, bezelWidth, CGRectMinXEdge);
return CGRectContainsPoint(leftBezelRect, point);
}
I understand that CGRectDivide function "Slices up a rect", but thats as far as I can make out.
I hope to get more clarification regarding the function. Also, how does the function return value vide a false / true value?

void CGRectDivide(
CGRect rect,
CGRect *slice,
CGRect *remainder,
CGFloat amount,
CGRectEdge edge
)
The CGRectDivide method splits a CGRect into two CGRects based on the CGRectEdge and distance from the rectangle side amount provided to the method.
Source

You should check
https://developer.apple.com/library/ios/documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html#//apple_ref/c/func/CGRectDivide
and
http://nshipster.com/cggeometry/
But it seems that this method could be simplified to
-(BOOL)isPointContainedWithinBezelRect:(CGPoint)point {
CGRect leftBezelRect = self.view.bounds;
leftBezelRect.size.width = 20;
return CGRectContainsPoint(leftBezelRect, point);
}
or even to
-(BOOL)isPointContainedWithinBezelRect:(CGPoint)point {
return CGRectContainsPoint(self.view.bounds, point) && (point.x <= 20);
}

Related

Ensure arbitrarily rotated CGRect fills another when rotation occurs

Update: partially working implementation below.
I've asked a couple questions on this previously here and here.
The first works great to determine if the "image" rect is sufficiently contained inside the "crop" rect.
The second works a little bit, but something's off in my implementation of it that it doesn't really work.
I'm now looking at the problem a little differently, and would like to change the behavior:
When the user begins to rotate the image, I'll run the check method (below) to determine if it needs fixing or not.
If it does need fixing, rather than waiting until the user has finished rotating it, I'd like to resize the image simultaneously to fit the bounds. Is there a simpler (or more reliable) way to implement this behavior?
I'm going to block rotation greater than 35ยบ in either direction so that we don't have to worry about severe enlargements.
Assumptions/Constraints:
I'm using AutoLayout
Point of rotation will be the center of the crop rect, which may or may not be the center of the image rect.
This demonstrates it working with a square crop, but the user can resize it to whatever, so I imagine it's going to bite me in the ass even more so when it's not square.
Code:
- (BOOL)rotatedView:(UIView*)rotatedView containsViewCompletely:(UIView*)cropView {
// If this method returns YES, good! if NO, bad!
CGPoint cropRotated[4];
CGRect rotatedBounds = rotatedView.bounds;
CGRect cropBounds = cropView.bounds;
// Convert corner points of cropView to the coordinate system of rotatedView:
cropRotated[0] = [cropView convertPoint:cropBounds.origin toView:rotatedView];
cropRotated[1] = [cropView convertPoint:CGPointMake(cropBounds.origin.x + cropBounds.size.width, cropBounds.origin.y) toView:rotatedView];
cropRotated[2] = [cropView convertPoint:CGPointMake(cropBounds.origin.x + cropBounds.size.width, cropBounds.origin.y + cropBounds.size.height) toView:rotatedView];
cropRotated[3] = [cropView convertPoint:CGPointMake(cropBounds.origin.x, cropBounds.origin.y + cropBounds.size.height) toView:rotatedView];
// Check if all converted points are within the bounds of rotatedView:
return (CGRectContainsPoint(rotatedBounds, cropRotated[0]) &&
CGRectContainsPoint(rotatedBounds, cropRotated[1]) &&
CGRectContainsPoint(rotatedBounds, cropRotated[2]) &&
CGRectContainsPoint(rotatedBounds, cropRotated[3]));
}
Taking even yet a different spin on this, I'm getting there. But as you can see in the .gif, eventually the calculations get out of whack because the rotation really isn't what I should be using to calculate the new size. How can I implement this with the correct geometry to ensure the image always resizes correctly? For time saving, I put this into an Xcode project so you don't have to fiddle around building your own: https://github.com/r3mus/RotationCGRectFix.git
- (IBAction)gestureRecognized:(UIRotationGestureRecognizer *)gesture {
CGFloat maxRotation = 40;
CGFloat rotation = gesture.rotation;
CGFloat currentRotation = atan2f(_imageView.transform.b, _imageView.transform.a);;
NSLog(#"%0.4f", RADIANS_TO_DEGREES(rotation));
if ((currentRotation > DEGREES_TO_RADIANS(maxRotation) && rotation > 0) || (currentRotation < DEGREES_TO_RADIANS(-maxRotation) && rotation < 0)) {
return;
}
CGAffineTransform rotationTransform = CGAffineTransformRotate(self.imageView.transform, rotation);
gesture.rotation = 0.0f;
if (gesture.state == UIGestureRecognizerStateChanged) {
CGFloat scale = sqrt(_imageView.transform.a * _imageView.transform.a + _imageView.transform.c * _imageView.transform.c);
if ((currentRotation > 0 && rotation > 0) || (currentRotation < 0 && rotation < 0))
scale = 1 + fabs(rotation);
else if (currentRotation == 0)
scale = 1;
else
scale = 1 - fabs(rotation);
CGAffineTransform sizeTransform = CGAffineTransformMakeScale(scale, scale);
CGPoint center = _imageView.center;
_imageView.transform = CGAffineTransformConcat(rotationTransform, sizeTransform);
_imageView.center = center;
}
}
Gif:

Animate multiple shapes in UIView

I have a custom class that inherit from UIView. In the draw method I draw several shapes including some circles. I want to animate the color (now stroke color) of the circles independent of each other, e.g. I would like the color of one or more the circles to "pulse" or flash (using ease-in/ease-out and not linearly).
What would be the best way to archive this?
It would be great to be able to use the built-in animation code (CABasicAnimation and the like) but I'm not sure how?
EDIT: Here's the code involved. (I am using Xamarin.iOS but my question is not specific to this).
CGColor[] circleColors;
public override void Draw (RectangleF rect)
{
base.Draw (rect);
using (CGContext g = UIGraphics.GetCurrentContext ()) {
g.SetLineWidth(4);
float size = rect.Width > rect.Height ? rect.Height : rect.Width;
float xCenter = ((rect.Width - size) / 2) + (size/2);
float yCenter = ((rect.Height - size) / 2) + (size/2);
float d = size / (rws.NumCircles*2+2);
var circleRect = new RectangleF (xCenter, yCenter, 0, 0);
for (int i = 0; i < rws.NumCircles; i++) {
circleRect.X -= d;
circleRect.Y -= d;
circleRect.Width += d*2;
circleRect.Height += d*2;
CGPath path = new CGPath ();
path.AddEllipseInRect (circleRect);
g.SetStrokeColor (circleColors [i]);
g.AddPath (path);
g.StrokePath ();
}
}
}
You need to move all your drawing code to a subclass of CALayer, and decide parameters which, once varied, will produce the desired animations. Convert these parameters to the layer's properties, and you can animate the layer's properties with CABasicAnimation (or even [UIView animateXXX]).
See this SO question for more information.
Make sure that you set the layer's rasterizationScale to [UIScreen mainScreen].scale to avoid blurs on Retina.

Rectangle and Circle collision detection

I am trying to do collision detection between a rectangle and a circle. I came up with this method:
-(BOOL) isCollidingRect:(CCSprite *) spriteOne WithSphere:(CCSprite *) spriteTwo {
float diff = ccpDistance(spriteOne.position, spriteTwo.position);
float obj1Radii = [spriteOne boundingBox].size.width/2;
float obj2Radii = [spriteTwo boundingBox].size.width/2;
if (diff < obj1Radii + obj2Radii) {
return YES;
} else {
return NO;
}
}
and this is how I check it:
if ([self isCollidingRect:player WithSphere:blocker] == true) {
[self playerdeathstart];
}
This seems to work properly on the side of the rectangle but it doesn't above or below it. On the top and bottom, the collision occurs too early.
Is there a way I can get this collision to detected properly? Thank you for your help.
You can use CGRectIntersectsRect to achieve this.
-(BOOL) isCollidingRect:(CCSprite *) spriteOne WithSphere:(CCSprite *) spriteTwo {
return CGRectIntersectsRect([spriteOne boundingBox],[spriteTwo boundingBox]);
}
It is not pixel perfect but as i understand that is not necessary in this case.
This is not a solution for those who use Cocos2d-ObjC, but will help for Cocos2d-x devs (for instance, personally I found this topic because was searching for the same for my c++ game).
Cocos2d-x has method "intersectsCircle" for Rect class.
Here is how I solved in my c++ project almost the same problem as one described by you:
bool ObstacleEntity::hasCollisionAgainst(cocos2d::Sprite *spr)
{
cocos2d::Rect rect = cocos2d::Rect( spr->getPositionX(), spr->getPositionY(), spr->getBoundingBox().size.width, spr->getBoundingBox().size.height);
float rw = this->getBoundingBox().size.width / 2;
float rh = this->getBoundingBox().size.height / 2;
float radius = ( rw > rh ) ? rw : rh;
cocos2d::Vec2 center( this->getPositionX() + rw, this->getPositionY() + rh );
return rect.intersectsCircle( center, radius );
}
Passed Sprite here is rectangle, while ObstacleEntity always is almost ideally round.
Note that anchor points for all entities are set to lower left corner in my case.

Auto-flowing layout with embedded controls and automatically breaking text

I am trying to create a layout for my iPhone and iPad app that automatically reflows based on dynamic data.
A bit more concrete, this means that I have several objects. Each object has a certain type and associated data. The data is what should be displayed, the type determines how it should be displayed. A type could be text, meaning it should be displayed as simple text without the possibility for interaction. Another type could be date, meaning it should be displayed as a control that lets the user select a date, when tapped.
Now, the problem is that the objects I want to display are dynamic in number and associated data, so I can't position the controls at static locations.
This jsfiddle shows what kind of layout I mean. Depending on the width of the div, the content automatically re-flows and the date input control appears in the middle of the text.
I couldn't find any control that supports a scenario like this - so how would I go about achieving this?
Here is an example of using CoreText to solve a partially similar problem. Perhaps that will point you in that direction.
- (CFArrayRef)copyRectangularPathsForPath:(CGPathRef)path
height:(CGFloat)height {
CFMutableArrayRef paths = CFArrayCreateMutable(NULL, 0,
&kCFTypeArrayCallBacks);
// First, check if we're a rectangle. If so, we can skip the hard parts.
CGRect rect;
if (CGPathIsRect(path, &rect)) {
CFArrayAppendValue(paths, path);
}
else {
// Build up the boxes one line at a time. If two boxes have the
// same width and offset, then merge them.
CGRect boundingBox = CGPathGetPathBoundingBox(path);
CGRect frameRect = CGRectZero;
for (CGFloat y = CGRectGetMaxY(boundingBox) - height;
y > height; y -= height) {
CGRect lineRect =
CGRectMake(CGRectGetMinX(boundingBox), y,
CGRectGetWidth(boundingBox), height);
CGContextAddRect(UIGraphicsGetCurrentContext(), lineRect);
// Do the math with full precision so we don't drift,
// but do final render on pixel boundaries.
lineRect = CGRectIntegral(clipRectToPath(lineRect, path));
CGContextAddRect(UIGraphicsGetCurrentContext(), lineRect);
if (! CGRectIsEmpty(lineRect)) {
if (CGRectIsEmpty(frameRect)) {
frameRect = lineRect;
}
else if (frameRect.origin.x == lineRect.origin.x &&
frameRect.size.width == lineRect.size.width) {
frameRect = CGRectMake(lineRect.origin.x, lineRect.origin.y, lineRect.size.width,
CGRectGetMaxY(frameRect) - CGRectGetMinY(lineRect));
}
else {
CGMutablePathRef framePath =
CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
CFArrayAppendValue(paths, framePath);
CFRelease(framePath);
frameRect = lineRect;
}
}
}
if (! CGRectIsEmpty(frameRect)) {
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
CFArrayAppendValue(paths, framePath);
CFRelease(framePath);
}
}
return paths;
}

How to correct the error of this program

I have a code but in this code there is an error and I don't know Why i has this error.
I put this code because i want my ball hit least the border soy the screens.
You can see my code :
CGPoint ballCenter = ball.center;
CGRect ballRect = CGRectMake(50, 73, 50, 50); // an arbitrary location for the ball, you would normally use the frame property of the ball here
CGRect s = [UIScreen mainScreen].bounds;
CGRect adjustedScreenRect = CGRectMake(s.x-50 // take the ball's (width or height) and divide it by two to get the distance to the center. Then multiply it by two and subtract from the appropriate dimension(x offset (width), y offset (height), width (width), height (height)).
BOOL isOnScreen = CGRectContainsPoint(adjutedScreenRect, ballCenter);
// do whatever with the BOOL from the above line...
I have an error at this line:
CGRect adjustedScreenRect = CGRectMake(s.x-50 // take the ball's (width or height) and divide it by two to get the distance to the center. Then multiply it by two and subtract from the appropriate dimension(x offset (width), y offset (height), width (width), height (height)).
BOOL isOnScreen = CGRectContainsPoint(adjutedScreenRect, ballCenter);
And the error is "no member named "x" in struct CGRect".
thanks you for your help
The answer is CGRect adjustedScreenRect = CGRectMake(s.origin.x-50
BOOL isOnScreen = CGRectContainsPoint(adjutedScreenRect, ballCenter);
But I have an error Expected ) on BOOL .
Can you help me
I think you mean s.origin.x, not s.x. Because CGRect is a struct of a CGPoint and a CGSize, directly accessing the x value of a CGRect is not possible without specifying which part of the struct you want to access first.
Edit:
You never actually closed the parenthesis, or satisfied all 4 arguments, of the CGRect. Try this:
CGRectMake(s.origin.x-50, s.origin.y, width,height);

Resources