I want to know is there a way to set background color for a SKLabelNode not font color. I'm looking for something like below mentioned code, which is available in ios apps.
label.backgroundColor = [UIColor redColor];
Try adding the SKLabelNode as a child of a SKSpriteNode.
SKLabelNode *label = [[SKLabelNode alloc]initWithFontNamed:#"Helvetica"];
label.position = CGPointMake(0, -label.frame.size.height/2);
SKSpriteNode *background = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(label.frame.size.width, label.frame.size.height)];
background.position = CGPointMake(200, 100);
[background addChild:label];
[self addChild:background];
Adding the SKLabelNode as a child of a SKSpriteNode works but it hides the text. So, I resolved this issue by setting the zPosition of background to a negative number. Here is the swift 3 code:
var label = SKLabelNode(fontNamed: "Helvetica")
label.position = CGPoint(x: CGFloat(0), y: CGFloat(-label.frame.size.height / 2))
var background = SKSpriteNode(color: UIColor.red, size: CGSize(width: CGFloat(label.frame.size.width), height:CGFloat(label.frame.size.height)))background.position = CGPoint(x: CGFloat(200), y: CGFloat(100))
background.zPosition = -1
label.addChild(background)
self.addChild(label)
be sure to include the text value before calculating the background’ GCSize from the label node, else the values returned will be zero as it has no text to determine the size of the label.
..correct about layer issues
Related
I've got a working clock with a pendulum, but the swinging arm does not scale correctly. Here's what it looks like before scaling...
And then, after scaling...
The clock face scales down, and so does the swing arm (the green line), but it looks like it scales around it's center point not the fulcrum at the center of the face. I can fix that scaling by setting the swing's anchorPoint so it scales toward one end rather than the center...
swing.anchorPoint = CGPointMake(0, 0.5);
but doing that causes the pendulum to not swing. (I think because physics forces are applied to swing's anchorPoint which, if I move it to one end, is at the pin anchor which is fixed).
How can I get the arm to scale towards the fulcrum and still allow it to swing?
Here's the code...
// in my SKScene
ClockFace *face = [ClockFace face]; // just an SKNode subclass
face.name = #"face";
face.position = CGPointMake(150, 150);
[self addChild:face];
// add the pendulum
SKSpriteNode *fulcrum = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[face addChild:fulcrum];
fulcrum.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fulcrum.frame.size];
fulcrum.physicsBody.dynamic = NO;
SKSpriteNode *swing = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(190, 2)];
[face addChild:swing];
swing.position = CGPointMake(95, 0);
swing.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:swing.frame.size];
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:fulcrum.physicsBody bodyB:swing.physicsBody anchor:face.position];
[self.physicsWorld addJoint:pinJoint];
In Swift 3 example of this:
let nodeSize = CGSize(width: 10, height: 10)
let node = SKSpriteNode(color: .red, size: nodeSize)
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.isDynamic = false
self.addChild(node)
let node2Size = CGSize(width: 60, height: 8)
let node2 = SKSpriteNode(color: .green, size: node2Size)
node2.position = CGPoint(x: 5, y: 0)
node2.anchorPoint = CGPoint(x: 0.0, y: 0.5) // <- New Line
node2.physicsBody = SKPhysicsBody(rectangleOf: node2Size)
node2.physicsBody?.mass = 1.0
self.addChild(node2)
// Scale Line
node2.run(SKAction.repeatForever(SKAction.sequence([
SKAction.scale(to: 0.2, duration: 1.0),
SKAction.scale(to: 1.5, duration: 0.5),
])))
// Anchor Point
let a = SKPhysicsJointPin.joint(withBodyA: node.physicsBody! , bodyB: node2.physicsBody!, anchor: CGPoint(x: 0.0, y: 0.0))
self.physicsWorld.add(a)
The reason why the scaling is weird is because of how you do your anchor point. Imagine a rubber band with a thumbtack in it. The anchor point is where the thumbtack is. Now to scale. Just grab the rubber band and pull. When the thumbtack is the middle, you will find it easy to stretch both sides evenly (this is what you are doing on the y axis). When you place it on the left of the rubber band, you will only be able to scale the right side (This is what you want to be doing)
Now with PhysicsBody, you need to adjust the bodies anchor point based on the sprites anchor point. To do this you need to do some math:
let centerPoint = CGPointMake(sprite.size.width / 2 - (sprite.size.width * sprite.anchorPoint.x), sprite.size.height / 2 - (sprite.size.height * sprite.anchorPoint.y))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size, center: centerPoint)
Using centerPoint allows you to move where the center of your body is.
// in my SKScene
ClockFace *face = [ClockFace face]; // just an SKNode subclass
face.name = #"face";
face.position = CGPointMake(150, 150);
[self addChild:face];
// add the pendulum
SKSpriteNode *fulcrum = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[face addChild:fulcrum];
fulcrum.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fulcrum.frame.size];
fulcrum.physicsBody.dynamic = NO;
SKSpriteNode *swing = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(190, 2)];
[face addChild:swing];
swing.anchorPoint = CGPointMake(0,0.5);
CGPoint *centerPoint = CGPointMake(swing.size.width / 2 - (swing.size.width * swing.anchorPoint.x), swing.size.height / 2 - (swing.size.height * swing.anchorPoint.y));
swing.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:swing.frame.size center:centerPoint];
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:fulcrum.physicsBody bodyB:swing.physicsBody anchor:face.position];
[self.physicsWorld addJoint:pinJoint];
I'm creating a UILabel to which I set the background color and corner radius with the following code:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
self.scoreLabel.layer.masksToBounds = YES;
self.scoreLabel.layer.cornerRadius = self.scoreLabel.frame.size.width/2;
self.scoreLabel.layer.borderWidth = 8.0;
self.scoreLabel.layer.borderColor = [[UIColor DISNavy] CGColor];
However the background's color seems to be leaking to the edge of the border (see image). Any ideas why? Any idea on how to fix it?
I was also facing the same problem. It was a silly mistake. I always forget to tick clipToBounds in case of cornerRadius.
So, just ticking the Clip to Bounds for UILabel in Storyboard fixed my problem.
And yes, we need to keep the below code too:
label.layer.masksToBounds = true
I ran into the same problem with the UIButton's background color leaking around the edge of its border.
Instead of setting the UIButton background color on the UIButton, set it on the UIButton's layer.
Replace:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
With this:
self.scoreLabel.layer.backgroundColor = [[UIColor DISRed] CGColor];// custom red`
I created my own UILabel and background colour does not seem to be leaking.
Write this in .h file of your project.
UILabel *label;
Write this in .m file of your project.
label=[[UILabel alloc]initWithFrame:CGRectMake(100, 300, 100, 100)];//Set frame of label in your viewcontroller.
[label setBackgroundColor:[UIColor redColor]];//Set background color of label.
[label setText:#"Label"];//Set text in label.
[label setTextColor:[UIColor blackColor]];//Set text color in label.
[label setTextAlignment:NSTextAlignmentCenter];//Set text alignment in label.
[label.layer setCornerRadius:50.0];//Set corner radius of label to change the shape.
[label.layer setBorderWidth:8.0f];//Set border width of label.
[label setClipsToBounds:YES];//Set its to YES for Corner radius to work.
[label.layer setBorderColor:[UIColor greenColor].CGColor];//Set Border color.
[self.view addSubview:label];//Add it to the view of your choice.
It is probably anti aliasing issue. you can better fix it by adding a bezier path around the corners.
CAShapeLayer *subLayer = [[CAShapeLayer alloc] init];
[subLayer setFillColor:[UIColor clearColor].CGColor];
[subLayer setStrokeColor:[UIColor whiteColor].CGColor];
[subLayer setLineWidth:1.0];
[subLayer setPath:[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.layer.cornerRadius].CGPath];
[imageView.layer addSublayer:subLayer];
For those who are still facing the issue of border color leaking out:
Go through the below code, please note you will need to set frames & border width as per your requirement, I'm setting the position as view's center
let badgeSize: CGFloat = 10
let redBadge = UIView(frame: CGRect(x: view.center.x, y:view.center.y, width: badgeSize, height: badgeSize))
redBadge.layer.borderColor = UIColor.white.cgColor
redBadge.layer.borderWidth = 2
redBadge.backgroundColor = .red
redBadge.layer.cornerRadius = badgeSize * 0.5
redBadge.clipsToBounds = true
redBadge.layer.masksToBounds = true
redBadge.maskLayerOnView(radius: badgeSize * 0.5)
view.addSubview(redBadge)
Secondly, we need to write an extension on UIView
extension UIView{
func maskLayerOnView(radius: CGFloat){
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.allCorners],
cornerRadii: CGSize(width: radius,
height: radius)).cgPath
self.layer.mask = maskLayer
}
}
This code snippet removes the border color separating out, one can replicate this behaviour on any kind of views.
For detailed explanation please see this article.
Well, a lot of answers...
I found the problem persists, as long as the UIViews background color is used and not the background color of the UIViews 'layer'. In addition, of course, masking needs to be enabled.
For a UICollectionViewCell subclass the code is:
- (void)prepare
{
self.contentView.layer.backgroundColor = UIColor.redColor.CGColor;
self.contentView.layer.cornerRadius = 25.0;
self.contentView.layer.borderColor = UIColor.blackColor.CGColor;
self.contentView.layer.borderWidth = 1.0;
//Enable to optimize image views: self.contentView.layer.shouldRasterize = YES;
//Enable to optimize image views: self.contentView.layer.rasterizationScale = UIScreen.mainScreen.scale;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}
To make the setting of the background color more comfortable and less error prone, some could add this:
/*
setBackgroundColor:
*/
- (void)setBackgroundColor:(UIColor *)p_BackgroundColor
{
super.backgroundColor = nil;
self.contentView.layer.backgroundColor = p_BackgroundColor.CGColor;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}
Here's a distance formula I run through the update method to keep track of the distance between 2 sprites:
-(void)distance
{
double dx = (_spriteA.position.x - _spriteB.position.x); //(x2 - x1);
double dy = (_spriteA.position.y - _spriteB.position.y); //(y2 - y1);
dist = sqrt(dx*dx + dy*dy);
}
-(void)update:(NSTimeInterval)currentTime
{
[self distance]; //Calculate A & B distance
if (dist > 50)
{
//What to write here to keep a constant distance between A & B?
[_spriteB runAction:[SKAction moveTo:(_spriteA.position) duration:1]];
}
}
Distance is tracked well but the if statement has problems. Specifically the actual action being run on sprite B. What ends up happening is sprite B moves toward sprite A in a yo-yo like fashion non-stop - even when sprite A isn't moving. I need sprite B to only move when sprite A is moved and retaining the distance of 50. Sprite A only moves when a person touches the screen. Please help.
You can use the SKConstraint class to maintain the distance between the two nodes. For example :
let node1 = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(20, 10))
node1.position = CGPointMake(self.size.width/2, self.size.height/2)
self.addChild(node1)
let node2 = SKSpriteNode(color: UIColor.blueColor(), size: CGSizeMake(10, 20))
node2.position = CGPointMake(self.size.width/2, self.size.height/2 - 50)
self.addChild(node2)
// The upper and lower limit is set to 50 to maintain a constant distance.
let constraint = SKConstraint.distance(SKRange(lowerLimit: 50, upperLimit: 50), toNode : node1)
node2.constraints = [constraint]
node1.runAction(SKAction.moveToY(100, duration: 2.0))
In Objective C
SKSpriteNode *node1 = [[SKSpriteNode alloc] initWithColor:[UIColor redColor]
size:CGSizeMake(20, 10)];
node1.position = CGPointMake(self.size.width/2, self.size.height/2);
[self addChild:node1];
SKSpriteNode *node2 = [[SKSpriteNode alloc] initWithColor:[UIColor redColor]
size:CGSizeMake(10, 20)];
node2.position = CGPointMake(self.size.width/2, self.size.height/2 - 50);
[self addChild:node2];
// The upper and lower limit is set to 50 to maintain a constant distance.
SKConstraint *constraint = [SKConstraint distance:[SKRange rangeWithLowerLimit:50 upperLimit:50] toNode:node1];
node2.constraints = #[constraint];
[node1 runAction:[SKAction moveToY:100 duration:2.0]];
I try to draw a single circle in my iOS SpriteKit project but the edges of the circle are not smooth. I would like to have a nicely drawn circle as I would draw it with Photoshop (anti-aliasing). I found several similar questions but the my problem remains.
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.backgroundColor = [SKColor whiteColor];
self.scaleMode = SKSceneScaleModeAspectFill;
self.size = CGSizeMake(640, 1136);
CGRect circle = CGRectMake(100.0, 100.0, 80.0, 80.0);
SKShapeNode *shapeNode = [[SKShapeNode alloc] init];
shapeNode.path = [UIBezierPath bezierPathWithOvalInRect:circle].CGPath;
shapeNode.fillColor = [SKColor redColor];
shapeNode.lineWidth = 0;
[self addChild:shapeNode];
}
return self;
}
What is the trick here?
The antialiasing is activated by default, but It only applies to the lines, not the fill.
Set the lineWidth to a number greater than 0 and try again.
Set the antialised property to YES.
shapeNode.antialiased = YES;
The rough edges are not visible when you use fill color.
//shapeNode.fillColor = [SKColor redColor];
shapeNode.strokeColor =[SKColor redColor];
shapeNode.antialiased = YES;
shapeNode.lineWidth = 1;
Now switch between YES and NO for antialisaed property.
You will notice the difference.
I have to drop a shadow to the right and bottom of uiview.Im doing this in interface builder.But I see the shadow dropped to top of it.Tried differnt sizes.but couldn't get it.
layer.masksToBound=No
layer.shadowOpacity=0.15
layer.shadowRadius=2
layer.shadowOffSet={10,-10} //Values being set in Interfacebuilder.
Still this drops shadow at top.What should I do to get at bottom of view.
Try the following code, it might help you
myView.layer.shadowColor = [UIColor purpleColor].CGColor;
myView.layer.shadowOffset = CGSizeMake(5, 5);
myView.layer.shadowOpacity = 1;
myView.layer.shadowRadius = 1.0;
myView.layer.maskToBounds = NO;
I tested this code and it's working and output is:
Hi I have used below code ,it will provide you with shadow you want.
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:_viewShadow.bounds];
_viewShadow.layer.masksToBounds = NO;
_viewShadow.layer.shadowColor = [UIColor blackColor].CGColor;
_viewShadow.layer.shadowOffset = CGSizeMake(10.0f, 5.0f); /*Change value of X n Y as per your need of shadow to appear to like right bottom or left bottom or so on*/
_viewShadow.layer.shadowOpacity = 0.5f;
_viewShadow.layer.shadowPath = shadowPath.CGPath;
Also masksToBounds is imp as it disables the clipping of sublayers that extend further than the view's bounds. If you put it YES then you won't see shadow as it clips sublayer where else in NO it allow to extent layer.
In Swift 3, CGSizeMake no longer exists. It has been changed to CGSize(width: 20, height: 10). So the shadowOffset can be set like this in Swift 3:
myView.layer.shadowOffset = CGSize(width: 20, height: 10)
I found out that these values give a nice result :
myView.layer.shadowColor = UIColor.black.cgColor
myView.layer.shadowOpacity = 0.25
myView.layer.shadowRadius = 3
myView.layer.shadowOffset = CGSize(width: 1, height: 1) // shadow on the bottom right
I think your shadow offset is incorrect. It should be { 10 , 10} like:
[layer setShadowOffset:CGSizeMake( 10 , 10 ) ];