I am a newbie to ios development and am fairly confused about how to position a UI Text field in swift. I am working with sprite kit and am creating a gaem that involves inputing a quadratic equation. I want to position my text fields along side my labels so it say y=_x^2+x+. When I run the following code, my text fields are in the top left corner! What should I do?
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blueColor()
//create y = label
let yLabel = SKLabelNode(fontNamed:"Note-Worthy-Bold")
yLabel.text = "Y = ";
yLabel.fontColor = SKColor.redColor()
yLabel.fontSize = 30;
yLabel.position = CGPoint(x:CGRectGetMidX(self.frame)-300, y:CGRectGetMidY(self.frame)+100);
self.addChild(yLabel)
//create text fielld for coefiecent of x^2 variable
var xSqrCoefInput = UITextField(frame:CGRectMake(10, 10, 30, 10))
xSqrCoefInput.backgroundColor = UIColor.orangeColor()
self.view?.addSubview(xSqrCoefInput)
//create x^2 Label
let xSqrLabel = SKLabelNode(fontNamed:"Note-Worthy-Bold")
xSqrLabel.text = "x^2+";
xSqrLabel.fontColor = SKColor.redColor()
xSqrLabel.fontSize = 30;
xSqrLabel.position = CGPoint(x:CGRectGetMidX(self.frame)-150, y:CGRectGetMidY(self.frame)+100);
self.addChild(xSqrLabel)
//create text fielld for coefiecent of x variable
var xCoefInput = UITextField(frame:CGRectMake(10, 10, 30, 10))
self.view?.addSubview(xCoefInput)
xCoefInput.backgroundColor = UIColor.orangeColor()
//create x label
let xLabel = SKLabelNode(fontNamed:"Note-Worthy-Bold")
xLabel.text = "x+";
xLabel.fontColor = SKColor.redColor()
xLabel.fontSize = 30;
xLabel.position = CGPoint(x:CGRectGetMidX(self.frame)-0, y:CGRectGetMidY(self.frame)+100);
self.addChild(xLabel)
//create text fielld for constant
var constantInput = UITextField(frame:CGRectMake(10, 10, 30, 10))
self.view?.addSubview(constantInput)
constantInput.backgroundColor = UIColor.orangeColor()
}
CGRectMake takes in 4 parameters.
CGRectMake(x position, y position, width, height)
The default starting position is the top-left corner. Your current textfield position is (10,10), so basically you just have to change the x and y positions for all the textfields.
var xSqrCoefInput = UITextField(frame:CGRectMake(10, 10, 30, 10))
If you wish to place the textfield next to the labels, it would be best to get the position of the labels and set the position from there.
Related
I am creating a custom renderer to display a title and description in a translucent white space on an image. The title should wrap to the second line if wider than the image, and an optional description may appear below if there is room. In Android I am able to do this using a StaticLayout:
// Create text rectangle
var height = Height / 3;
canvas.Save();
canvas.ClipRect(0, Height - height, Width, Height);
canvas.DrawARGB(191, 255, 255, 255);
canvas.Restore();
var item = ((ImageTile) Element).Item;
var textSize = (height - 15) / 2;
var textPaint = new TextPaint
{
StrokeWidth = 5,
TextSize = textSize,
FakeBoldText = true,
};
if (Build.VERSION.SdkInt >= BuildVersionCodes.Honeycomb)
SetLayerType(LayerType.Software, textPaint);
textPaint.SetStyle(Paint.Style.Fill);
textPaint.Color = global::Android.Graphics.Color.Black;
// init StaticLayout for text
var titleLayout = new StaticLayout(
item.Title, textPaint, Width - 10, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, false);
canvas.Translate(5, Height - height + 5);
titleLayout.Draw(canvas);
canvas.Restore();
textPaint = new TextPaint
{
StrokeWidth = 4,
TextSize = textSize - 10,
};
if (Build.VERSION.SdkInt >= BuildVersionCodes.Honeycomb)
SetLayerType(LayerType.Software, textPaint);
var descLayout = new StaticLayout(
item.Description, textPaint, Width - 10, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, false);
canvas.Translate(5, Height - height + titleLayout.Height + 5);
descLayout.Draw(canvas);
canvas.Restore();
in iOS I am using CATextLayers, but I am unable to get the text to wrap even thought I define the frame and set both Wrapped to true and TextTruncationMode to None. I also don't know how to get the actual height of the tilteLayer so I can position the descLayer below it. This is what I have so far, which draws the title and description on top of each other without wraping.
var textLayer = new CALayer();
var textRec = new CGRect(0, element.HeightRequest - textheight, element.WidthRequest,
textheight);
textLayer.Frame = textRec;
var backgroundcolor = Color.FromRgba(255, 255, 255, .25).ToCGColor();
textLayer.BackgroundColor = backgroundcolor;
Control.Layer.AddSublayer(textLayer);
var titleLayer = new CATextLayer
{
String = element.Item.Title,
ForegroundColor = Color.Black.ToCGColor(),
FontSize = 14,
Wrapped = true,
TextTruncationMode = CATextLayerTruncationMode.None,
TextAlignmentMode = CATextLayerAlignmentMode.Left,
//Bounds = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
// textheight - 4),
};
var titleRec = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
textheight - 4);
titleLayer.Frame = titleRec;
Control.Layer.AddSublayer(titleLayer);
var descLayer = new CATextLayer
{
String = element.Item.Description,
ForegroundColor = Color.Black.ToCGColor(),
FontSize = 12,
Wrapped = true,
TextTruncationMode = CATextLayerTruncationMode.None,
};
var descRec = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
textheight - 4);
descLayer.ContentsRect = descRec;
Control.Layer.AddSublayer(descLayer);
Why not try autoLayout? You want to add a background view and two types of text on the original image. CALayer may achieve your effect, but it can't use autoLayout so that you need to use hard code(calculate the text height and the layer's position) to construct that. Also you said
I am unable to get the text to wrap. And I also don't know how to get
the actual height of the tilteLayer so I can position the descLayer
below it.
In the image's custom renderer, since this control has not been rendered, its Frame and HeightRequest are also unknown. Then you won't get the correct frame neither the layer, so the text will not be shown. I think the best way to do that is using AutoLayout:
// Create a view to hold content just like your textLayer
UIView bgView = new UIView();
bgView.BackgroundColor = UIColor.FromRGBA(1, 1, 1, 0.25f);
bgView.TranslatesAutoresizingMaskIntoConstraints = false;
Control.AddSubview(bgView);
bgView.LeadingAnchor.ConstraintEqualTo(Control.LeadingAnchor).Active = true;
bgView.TopAnchor.ConstraintGreaterThanOrEqualTo(Control.TopAnchor).Active = true;
bgView.TrailingAnchor.ConstraintEqualTo(Control.TrailingAnchor).Active = true;
bgView.BottomAnchor.ConstraintEqualTo(Control.BottomAnchor).Active = true;
UILabel titleLabel = new UILabel();
bgView.AddSubview(titleLabel);
titleLabel.TranslatesAutoresizingMaskIntoConstraints = false;
// Set this property to 0, then your label will move to several lines if your text is too large.
titleLabel.Lines = 0;
titleLabel.Font = UIFont.SystemFontOfSize(14);
titleLabel.Text = Element.Item.Title;
titleLabel.LeadingAnchor.ConstraintEqualTo(bgView.LeadingAnchor).Active = true;
titleLabel.TopAnchor.ConstraintEqualTo(bgView.TopAnchor).Active = true;
titleLabel.TrailingAnchor.ConstraintEqualTo(bgView.TrailingAnchor).Active = true;
// This constraint will show the titleLabel's content at high priority. It means show the descLabel if the image has enough place.
titleLabel.SetContentHuggingPriority(249, UILayoutConstraintAxis.Vertical);
UILabel descLabel = new UILabel();
bgView.AddSubview(descLabel);
descLabel.TranslatesAutoresizingMaskIntoConstraints = false;
descLabel.Lines = 0;
descLabel.Text = Element.Item.Description;
descLabel.Font = UIFont.SystemFontOfSize(12);
descLabel.LeadingAnchor.ConstraintEqualTo(bgView.LeadingAnchor).Active = true;
descLabel.TopAnchor.ConstraintEqualTo(titleLabel.BottomAnchor).Active = true;
descLabel.TrailingAnchor.ConstraintEqualTo(bgView.TrailingAnchor).Active = true;
descLabel.BottomAnchor.ConstraintEqualTo(bgView.BottomAnchor).Active = true;
In this way, bgView will expand its height depending on the titleLabel and descLabel. The largest height will be the original image's height. Moreover titleLabel will auto calculate its size depending on its content. Also the descLabel will always lies below the titleLabel if the room allows.
You can adjust these constraints to fit your own requirement.
I am wanting to create a SKLabelNode that is in the top left corner of every device. Also, to the left of the label in the top left corner I want an image that is a coin to show that this is the current coin count label. Every time I place my label in the corner on my iPhone 6s+ it isn't in the corner on my iPad.
Here is my code so far:
cornerCoin.position = CGPoint(x: screenWidth / -3.1, y: screenHeight / 3.175)
cornerCoin.zPosition = 10
cameraNode.addChild(cornerCoin)
coinLabel.position = CGPoint(x: screenWidth / -2.4, y: screenHeight / 8)
coinLabel.zPosition = 1
coinLabel.fontSize = 50
coinLabel.fontColor = UIColor.black
coinLabel.fontName = "04b19"
cameraNode.addChild(coinLabel)
Personally, I would not be tracking screenWidth/screenHeight, it goes against the foundation of SpriteKit to begin with (Your scene behaves as your virtual screen, this is how you should handle everything inside of it) Instead, what you should do is convert from screen to scene, this way regardless of scale mode or anchor point, your label is where you want it to be.
To get the top right corner of your view converted to your scene, you do
if let view = scene.view
{
let topRightPos = view.convert(CGPoint(x:view.frame.maxX,y:view.frame.minY),to:scene)
let camTopRight = scene.convert(topRightPos,to:cameraNode)
}
Now we know where our top right position is:
We can do
if let view = scene.view
{
let topRightPos = view.convert(CGPoint(x:view.frame.maxX,y:view.frame.minY),to:scene)
let camTopRight = scene.convert(topRightPos,to:cameraNode)
coinLabel.position = camTopRight
coinLabel.zPosition = 1
coinLabel.fontSize = 50
coinLabel.fontColor = UIColor.black
coinLabel.fontName = "04b19"
coinLabel.horizontalAlighmentMode = .right
coinLabel.verticalAlighmentMode = .top
cameraNode.addChild(coinLabel)
}
to right align our label so that the text always extends to the left, then to add the coin, we do:
if let view = scene.view
{
let topRightPos = view.convert(CGPoint(x:view.frame.maxX,y:view.frame.minY),to:scene)
let camTopRight = scene.convert(topRightPos,to:cameraNode)
coinLabel.position = camTopRight
coinLabel.zPosition = 1
coinLabel.fontSize = 50
coinLabel.fontColor = UIColor.black
coinLabel.fontName = "04b19"
coinLabel.horizontalAlighmentMode = .right
coinLabel.verticalAlighmentMode = .top
cameraNode.addChild(coinLabel)
cornerCoin.position = CGGPoint(x:0.0,y:coinLabel.frame.midY)
cornerCoin.anchorPoint = CGPoint(x:1.0,y:0.5)
cornerCoin.zPosition = 10
cameraNode.addChild(cornerCoin)
}
Our label should now be in the top right position of our camera, and the coin should be to the left of our label centered vertically with the text.
You may want to play with how your label is positioned if you do not like the look of top alignment.
I am trying to get my "word" to float across the screen; constant velocity, no impacts, no gravity, no friction. Everything works except the word slows down.
Code for creating word:
func createWordNode (word: String, atPos: CGPoint) -> SKSpriteNode {
let doneSize = CGSize(width: 50, height: 50)
let wordSprite = SKSpriteNode()
wordSprite.size = CGSize(width: doneSize.width * CGFloat(word.len()), height: doneSize.height)
wordSprite.position = atPos
wordSprite.blendMode = .replace
wordSprite.zPosition = zlvlBG + 1
let ltrs = Array(word.uppercased().characters)
for i in 0 ... ltrs.count - 1 {
let done = SKSpriteNode(imageNamed: "LetterTiles/Tile" + String(ltrs[i]) + ".png")
done.size = doneSize
done.position = CGPoint(x: doneSize.width * CGFloat(Double(i) - 1.5), y: 0)
done.blendMode = .replace
done.zPosition = zlvlBG + 1
wordSprite.addChild(done)
}
wordSprite.physicsBody?.restitution = 1
wordSprite.physicsBody?.friction = 0
wordSprite.physicsBody?.linearDamping = 0
wordSprite.physicsBody?.angularDamping = 0
wordSprite.physicsBody?.mass = 2000
wordSprite.physicsBody = SKPhysicsBody(rectangleOf: wordSprite.size)
wordSprite.physicsBody?.collisionBitMask = 0
wordSprite.physicsBody?.contactTestBitMask = 0
wordSprite.physicsBody?.categoryBitMask = categoryWords
wordSprite.physicsBody?.fieldBitMask = 0
wordSprite.physicsBody?.isDynamic = true
wordSprite.physicsBody?.affectedByGravity = false
wordSprite.physicsBody?.allowsRotation = false
var velocity = CGVector()
velocity.dx = 100
velocity.dy = 0
wordSprite.physicsBody?.velocity = velocity
wordSprite.physicsBody?.applyImpulse(velocity)
wordSprite.name = "Word:" + word
return wordSprite
}
I call function like:
addChild (createWordNode(word: "Done", atPos: CGPoint(x:-500, y:450)))
Any ideas why word slows down?
Thanks.
It looks like you are creating an object that doesn't need to be in the physics simulation. So you may want to reconsider simply dropping the physics related code and update the nodes position manually.
That being said if you wish this node to remain in the Physics simulation. You need to add the SKPhysicsBody to the SKSpriteNode before you start trying to modify properties. e.g.
wordSprite.physicsBody = SKPhysicsBody(rectangleOf: wordSprite.size)
wordSprite.physicsBody?.restitution = 1
wordSprite.physicsBody?.friction = 0
wordSprite.physicsBody?.linearDamping = 0
wordSprite.physicsBody?.angularDamping = 0
wordSprite.physicsBody?.mass = 2000
I worked with sprite a little bit. But, I am not an expert. Does it work for you to use an action instead of velocity as the following for example:
var moveRight = SKAction.moveTo(CGPointMake(400, 0), duration:2.0)
wordSprite.runAction(moveRight)
One thing to point out too. Why do you use applyImpulse. velocity should be enough. Did you try to remove it?
I am making an IOS App in which I am rotating an imageView. Now, All things are complete. But when the rotation is completed. x, y, of that image View is changing .. How to resolve it . I am attaching code and screenshots so that you can easily find my problem.
let i = defaults.integerForKey("myown")
let g = nViews[i].frame
imageViews[i].transform = CGAffineTransformRotate(imageViews[i].transform, recognizer.rotation)
// nViews[i].transform = CGAffineTransformRotate(imageViews[i].transform, recognizer.rotation)
//
recognizer.rotation = 0
if recognizer.state == UIGestureRecognizerState.Ended
{
let f = imageViews[i].frame
// print(f.size.width) -->111.576689146311
// print(f.size.height) -->111.576689146311
// nViews[i].frame = f
nButtons[i].frame = CGRect(x: f.origin.x - 10, y: f.origin.y - 10, width: 25, height: 25)
// imageView.addSubview(nViews[i])
imageView.addSubview(nButtons[i])
}
Now the another view which is on that imageView become rectangular because of that changes.. As shown in screenshot first time when imageView is not rotated the view is perfect but when it rotates view becomes rectangular.. Hope you will understand problem now
Try to use bounds instead of frame: let g = nViews[i].bounds and let f = imageViews[i].bounds. See When to Use Bound and When to use Frame
I am pretty new to drawing programmatically. I've managed to draw a line graph, complete with points, lines, auto-scaling axes, and axis labels. In other screens, I can change characteristics of the graph and when I return to the screen, I refresh it with setNeedsDisplay() in the viewWillAppear function of the containing viewController. The lines are redrawn perfectly when I do this.
The new data that is added in other screens may require rescaling the axes. The problem is that when the graph is redrawn, the number labels on the axes are just added to the graph, without removing the old ones, meaning that some labels may be overwritten, while some old ones just remain there next to the new ones.
I think I see why this happens, in that I am creating a label and adding a subview, but not removing it. I guess I figured that since the lines are erased and redrawn, the labels would be, too. How do I cleanly relabel my axes? Is there a better way to do this? My function for creating the labels is listed below. This function is called by drawRect()
func createXAxisLabels(interval: Float, numIntervals: Int) {
let xstart: CGFloat = marginLeft
let yval: CGFloat = marginTop + graphHeight + 10 // 10 pts below the x-axis
var xLabelVals : [Float] = [0]
var xLabelLocs : [CGFloat] = [] // gives the locations for each label
for i in 0...numIntervals {
xLabelLocs.append(xstart + CGFloat(i) * graphWidth/CGFloat(numIntervals))
xLabelVals.append(Float(i) * interval)
}
if interval >= 60.0 {
xUnits = "Minutes"
xUnitDivider = 60
}
for i in 0...numIntervals {
let label = UILabel(frame: CGRectMake(0, 0, 50.0, 16.0))
label.center = CGPoint(x: xLabelLocs[i], y: yval)
label.textAlignment = NSTextAlignment.Center
if interval < 1.0 {
label.text = "\(Float(i) * interval)"
} else {
label.text = "\(i * Int(interval/xUnitDivider))"
}
label.font = UIFont.systemFontOfSize(14)
label.textColor = graphStructureColor
self.addSubview(label)
}
}
drawRect should just draw the rectangle, not change the view hierarchy. It can be called repeatedly. Those other labels are other views and do their own drawing.
You have a couple of options
Don't use labels -- instead just draw the text onto the rect.
-or-
Add/remove the labels in the view controller.
EDIT (from the comments): The OP provided their solution
I just replaced my code inside the for loop with:
let str : NSString = "\(xLabelVals[i])"
let paraAttrib = NSMutableParagraphStyle()
paraAttrib.alignment = NSTextAlignment.Center
let attributes = [
NSFontAttributeName: UIFont.systemFontOfSize(14),
NSForegroundColorAttributeName: UIColor.whiteColor(),
NSParagraphStyleAttributeName: paraAttrib]
let xLoc : CGFloat = CGFloat(xLabelLocs[i] - 25)
let yLoc : CGFloat = yval - 8.0
str.drawInRect(CGRectMake(xLoc, yLoc, 50, 16), withAttributes: attributes)