Why is my rectangle being drawn wrong? - ios

I'm trying to draw a rectangle around my screen. Here's my code:
let viewRect = SKShapeNode(rect: self.view!.frame)
viewRect.strokeColor = SKColor.whiteColor()
viewRect.lineWidth = 2.0
addChild(viewRect)
I'm using self.view!.frame instead of self.frame because my scene is not the same size as my screen (the scene is stretched to fill my screen).
I would have expected the code to draw a rectangle around my screen, but it draws this instead (the white rectangle is what is drawn):
Anyone know why it's not being drawn around my screen?
Scene size: 1024x768
View size: 414x736
Scale mode: Aspect Fill

You are creating your border as a node within the scene but then setting its coordinates according to the view that is presenting the scene. The SKScene's coordinate system is not generally the same as the view presenting it so that is why the shape is different to what you expect.

Related

CALayer with different effects between iPhone / iPad

This has me a bit confused. I have a universal app which has two different Storyboards for iPhone and iPad. I'm using a small block of CALayer code to round the edges of a view and throw a shadow on it. Works perfectly on the iPhone.
[self configShadowLayer:_view
cornerRadius:_view.frame.size.width / 2.0
shadowOffsetX:0.0
shadowOffsetY:3.0
shadowRadius:2.0
opacity:0.2];
-(void)configShadowLayer:(UIView *)shadowView
cornerRadius:(float)cornerRadius
shadowOffsetX:(float)shadowOffsetX
shadowOffsetY:(float)shadowOffsetY
shadowRadius:(float)shadowRadius
opacity:(float)opacity {
CALayer *shadowLayer = shadowView.layer;
shadowLayer.masksToBounds = NO;
shadowLayer.cornerRadius = cornerRadius;
shadowLayer.shadowOffset = CGSizeMake(shadowOffsetX, shadowOffsetY);
shadowLayer.shadowRadius = shadowRadius;
shadowLayer.shadowOpacity = opacity;
}
On the iPad, it gives me a diamond shape. I have to change the corner radius from the half to a quarter and then it works. But then of course the iPhone view goes from a circle to a rounded rect.
What am I missing?
You should call configShadowLayer:cornerRadius:shadowOffsetX:shadowOffsetY:shadowRadius:opacity: after the layout of the view is done. When you call it only before the layout process the view's frame has probably the wrong sizes, and you will get a corner radius which is too large.
BTW: You should compute the radius depending to the bounds of the view, because the bounds represents the coordinate system inside of the view.

Preserve CAShapeLayer's lineWidth in a zoomable UIView

I have a custom UIView, which has a UIImageView as its' subview to display an image and a CAShapeLayer as a sublayer to draw on it. The UIView is zoomable, I use a UIPinchGestureRecognizer to zoom in and out of the view. I can draw straight lines on the view using a CGPathRef. The problem is, when I pinch to zoom the view, the lines I draw also zoom and become thick. How to zoom in the view, keeping the lines thin?
Before zooming (green lines are the ones I draw):
And when I zoom:
What I want is, the lines to be as thin as they were before I zoomed in.
I tried to:
CGMutablePath path = CGPathCreateMutable();
//line drawing code here
//scale path to 1.0?
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);
CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &transform);
self.drawingLayer.path = transformedPath;
CGPathRelease(path);
CGPathRelease(transformedPath);
How do I prevent path width from enlarging when zooming?
You need to divide the line width with the zoom factor.By default line width is 1.
So create property for the same and when you apply zoom with gesture divide it.
i.e zoom = 4.0 then line width = 1.0/4.0
and same as if zoom is 2 then 1.0/2.0
Hope it is helpful

SpriteKit layout issues (Swift)

I'm new to SpriteKit and Swift (done a lot in Obj-C in the past for iOS) but I'm struggling on something that should be very basic.. positioning sprites on the screen!
Centering a label on the screens seems easy enough:
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
But when I try to put in the Bottom-left or top-right of the screen it isn't visible:
//Bottom-left label
myLabel.horizontalAlignmentMode = .Left
myLabel.position = CGPoint(x: 0.0, y: self.size.height)
or
//Top-right label
myLabel.horizontalAlignmentMode = .Right
myLabel.position = CGPoint(x: self.size.width, y: self.size.height)
This is with a new project, no other code. What am I doing wrong, please?
What you have to keep in mind is that a SpriteKit scene by default takes up far more than the screen. Its default size is 1024 by 768 and everything is scaled for universality unless you tell it otherwise. In your view controller you could see how the scene is initialized. You will see that it probably has a statement that says something like
scene.scaleMode = .AspectFill
This is the source of this. Apple in the documentation explains this in a straightforward manner what happens when you use AspectFill scaleMode:
The scaling factor of each dimension is calculated and the larger of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire area of the view is filled but may cause parts of the scene to be cropped.
There are other scale Modes to explore, which you can try but Aspect Fill has a large advantage with its universality.
You can specify the size of an SKScene when you create it. Typically you pass the size of the containing SKView so it covers the entire view. If the device rotates, your view is resized (if your app allows the new orientation). I'm not sure if the SKScene if resized. But either way, you need to update the position of your scene contents so that for example the label is in the top-right with the new dimensions.
The view controller whose view presents the scene receives the message viewWillTransitionToSize(_ size:,withTransitionCoordinator coordinator:). You can subclass this method to send a message to a custom method of the presenting SKScene that the size is changing. Then, you can simply reposition the node using the new size since self.frame will update.
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
Back to the core of the question! How to position a label in the top-right of the scene? Take into account that the scene origin is bottom-left. For SKLabelNode, the center is really in the center. So, offset by half of the label's dimensions.
myLabel.position = CGPoint(x: self.size.width - myLabel.size.width /2,
y: self.size.height - myLabel.size.height/2)
By the way, I have never seen an iOS game which uses a sprite based engine (such as Sprite Kit or Cocos2d) which supports device rotation/multiple screen sizes when running on the same device (iPhone/iPad).

Instead of sizeToFit what should I use

I'm working on an app where the main view fills up the whole screen. This main view contains subviews that you could think of as sprites. They wander around using their own code. The logic of how they wander around could have them wandering off screen. What I'd like to do is to resize the main view so that it encloses all the subviews. It's really not the view size that is changing, though. It is the scaling that is changing. Kind of like zooming out of a google maps view. How do I do that? It will be a gesture recognizer that triggers this.
You should do this using a three-level view hierarchy:
top-level view
scaled view
sprite view 0
sprite view 1
sprite view 2
etc.
Set the bounds of the scaled view to encompass the frames of all of the sprite views. Then set the transform of the scaled view so that it fits in the top-level view.
- (void)updateBoundsAndTransformOfView:(UIView *)scaledView {
CGRect scaledBounds = CGRectNull;
for (UIView *spriteView in scaledView.subviews) {
scaledBounds = CGRectUnion(scaledBounds, spriteView.frame);
}
scaledView.bounds = scaledBounds;
// Now scaledView's size is large enough to contain all of its subviews,
// and scaledView's coordinate system origin is at the left edge of the
// leftmost sprite and the top edge of the topmost sprite.
UIView *topLevelView = scaledView.superview;
CGRect topLevelBounds = topLevelView.bounds;
CGFloat xScale =topLevelBounds.size.width / scaledBounds.size.width;
CGFloat yScale = topLevelBounds.size.height / scaledBounds.size.height;
CGFloat scale = MIN(xScale, yScale);
scaledView.transform = CGAffineTransformMakeScale(scale, scale);
scaledView.center = CGPointMake(CGRectGetMidX(topLevelBounds), CGRectGetMidY(topLevelBounds));
}

Zoom in animation - c4framework

I am playing with the deferent animations using the alpha C4 framework.
I am trying to shape grow from the midel of the canvas till it covers it completely....Is there any animation that can create that zoom effect?
Yup, you can change and animate a C4Shape like this:
shape.animationDuration = 1.0f; //1 second animation duration
[shape rect:(-1,-1,769,1025)];
Note, the above is for a rectangle that will be 1px larger than the screen of an iPad on all sides. To make an ellipse you'd have to figure out the size of the frame that will encompass an ellipse that covers the whole screen.

Resources