How do you add an outline to text programmatically? - ios

I have two apps, one with a UILabel and one using SpriteKit with a SKLabelNode. I'd like to add a black outline around the white text.
I can't find any outline or border properties or anything like that within swift. I've tried just creating new labels with slightly bigger, black font behind them but that didn't look right at all.
Here is my game with SpriteKit
title.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
title.text = "Tap to start!"
title.fontName = "Arial"
title.zPosition = 10
title.fontSize = 50
self.addChild(title)
And here is my other one that uses UILables (It's within a red rectangle so it's easier to see)
let rectangle = UIView(frame:CGRect(x: self.view.frame.size.width / 2 - 150, y: self.view.frame.size.height / 2 - 75, width: 300, height: 150))
rectangle.backgroundColor = UIColor.red
self.view.addSubview(rectangle)
let label = UILabel(frame: CGRect(x: rectangle.frame.size.width / 2 - 75, y: rectangle.frame.size.height / 2 - 10, width: 150, height: 20))
label.textAlignment = .center
label.text = "Tap to Start!"
label.textColor = UIColor.white
label.font = UIFont(name: "Arial", size: 20)
rectangle.addSubview(label)
How do I outline these labels?

For SKLabelNode's, there is no 'easy' way of outlining text. There is however a 'hack'. It involves adding 8 additional duplicate nodes around the label that are coloured black (or whatever colour you want the outline to be), have a zPosition less than the original and otherwise, be identical.
It's important that each label has the same font size as the original.
Your attempt at doing this involved increasing the font size of your one outline copy label. As you said in the question, it doesn't work. You have to use multiple labels so the effect appears seamlessly.
You would place one copy directly above the original, one directly below, one to the left, one to the right, then one on each corner (top right, top left, bottom right, and bottom left). This gives the effect of an outline, however is not efficient and should be taken with a grain of salt.
Note: Keep the amount shifted from the original consistent for all the copies, so the 'outline' is evenly sized.
This should work for UILabels too, however I think there might be an easier way to do this with UILabels.

Related

Add label to point

I have followed this tutorial: https://www.raywenderlich.com/410-core-graphics-tutorial-part-2-gradients-and-contexts
I am trying to add a label over each point with the Int for the point, but it wont show. Adding code in the //Draw the circles on top of graph stroke
//Draw the circles on top of graph stroke
for i in 0..<graphPoints.count {
var point = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
point.x -= Constants.circleDiameter / 2
point.y -= Constants.circleDiameter / 2
let circle = UIBezierPath(ovalIn: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
circle.fill()
//let label = UILabel(frame: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
let label = UILabel()
label.frame.origin = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
label.text = "TDDDDDDDE"
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
self.addSubview(label)
self.view.addSubview(label)
}
Can anyone help me with adding the label? The label wont show, and I want it over the points.
Best guests, given there's not the full context. I see 3 possible reasons
1: You should replace
self.addSubview(label)
self.view.addSubview(label)
By
self.addSubview(label)
The second line will remove the label from view to add it to self.view, which might be a problem if they are different.
2: With the following line, you're telling the label to use autolayout ...but do not provide any constraint, so they might be there but not where you expected them to be.
label.translatesAutoresizingMaskIntoConstraints = false
To add constraints: see official Apple Doc, example below:
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: aConstant).isActive = true
But 3: your label is initialised without a frame/an empty frame. Call sizeToFit on your label after having set the content, fonts, etc if you don't use autolayout. If you stick to autolayout, this point is not applicable.
And another point indirectly linked to your question: If you manage to get your code to work, you'll probably have too much labels: your code seems to be in the draw method, which means that you'll be instantiating new labels each time the view is rendered ...and the previous ones won't be removed. A better practice would be instanciate the labels when you load the data (if they depend on the data points), to position them in layoutSubviews or to skip the labels entirely and use NSAttributedString draw functions to render the text directly in the view.

How to create a limit for the UILabel in swift

I want to explain my question with codes. First you can see the custom label codes in the below;
let resultLabel : UILabel = {
let lbl = UILabel()
lbl.textColor = .white
lbl.textAlignment = .right
lbl.text = "0"
lbl.font = UIFont(name: "Helvetica Neue", size: 35)
lbl.minimumScaleFactor = 0.75
lbl.lineBreakMode = .byClipping
lbl.adjustsFontSizeToFitWidth = true
return lbl
}()
I just created label with these lines of codes. I also give them specific height according to view's frame height size and padding left and the right you'll see the constraint codes in the below.
resultLabel.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: view.frame.size.height / 7, paddingLeft: 2, paddingBottom: 0, paddingRight: 5, width: 0, height: view.frame.size.height / 7)
I'll assign value into UILabel according to clicking button so i want to set chracter limit for the UILabel. According to code that i written it cropped data as i wanted but it didn't do that same action in the value. How can achieve of that could you help me out? Thanks.
What you want is not possible out of the box, and would be really strange in my opinion. Now the data and its presentation are separated, as they should be. Imagine, if your view controller size changed (the device got rotated, or your app is run on iPad in split mode, and the user resized the apps) - then you would be able to see more of your data. Because the label keeps the original data, everything would happen automatically. If it cropped the data to match its rendered representation, you would need to react to all such changes and reassign the original data to the label.
To provide a real world example - imagine you are looking at a huge (building sized) billboard, through a small (lets size, letter paper sized) window. You can see only a portion of the billboard, but everything is still there. In the same way, a UILabel is just a "window" onto the data provided.
Now, implementing this won't be trivial. Such a UIQuantumLabel* would need to take into consideration the width of each letter/digit/emoji/character in the data for current font at its current size (unless you were to limit it to monospaced fonts), take into consideration whether the automatic font scaling has happened and at what magnitude, whether the user has decreased/increased the font size in devices accessibility settings, and probably some more factors.
What is your use case for this? Why would you want to crop your data, and not keep the original?
*UIQuantumLabel because it reminded me of quantum mechanics (at least what I remember of it), where observing an object changes it state :)

Sizing of Live Views in Swift Playground

I'm having trouble figuring out how to lay out views in Swift Playgrounds for iPad, though this may also be relevant to Mac users.
The following code should create a view with a red square (also a view) that is near the edges of its' super view, but not touching them.
let v = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
let sqv = UIView(frame: CGRect(x: 400, y: 400, width: 50, height: 50))
sqv.backgroundColor = .red
v.addSubview(sqv)
PlaygroundPage.current.liveView = v
The result is not what you'd expect:
I suspect I know what is going on here; live views are at a fixed size that is larger than the display area. Some characteristics of the view are ignored when it is acting as the live view. However, I can't find where this is mentioned in the documentation, which vexes me. More importantly, how do I deal with this? I would like to be able to layout simple UIs that change to fit the current size of the live view. I don't know how to address this issue without trial & error and hardcoding, which are two things I would really like to avoid.
I suspect I know what is going on here; live views are at a fixed size that is larger than the display area.
Actually it's more like the other way around. An iPad screen is 1024 points wide (in landscape orientation). The right-hand pane (where it shows your live view) is 512 points wide. The playground forces your root view (v) to fill that pane, inset by 40 points on the left, right, and top (and more on the bottom). So your root view's width is forced to 432 ( = 512 - 2 * 40), less than the 500 you specified.
Views created in code (like yours) have translatesAutoresizingMaskIntoConstraints = true, and a resizing mask of 0, which means don't adjust the view's frame at all when its parent is resized. So the playground resizes your root view to width 432, but your root view doesn't move or resize its subview (sqv).
The easiest fix is to set the autoresizing mask of the subview to express your intent that it remain near the right and bottom edges of the root view. That means it should have flexible top and left margins:
let v = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
let sqv = UIView(frame: CGRect(x: 400, y: 400, width: 50, height: 50))
sqv.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin]
sqv.backgroundColor = .red
v.addSubview(sqv)
PlaygroundPage.current.liveView = v
Result:
let sqv = UIView(frame: CGRect(x: UIScreen.mainScreen().bounds.width-50-1, y:400, width: 50, height: 50))
The above code places your subview 1 point away from the right of the main view. Try changing the value 1 after 50 in x to desired value.

How do I set a layout margin for a text field in swift?

I am attempting to create margins in my text field so that when I go to type, the text isn't pressed so tightly against the edge.
I tried using this code (above viewDidLoad)
var insets = UIEdgeInsetsMake(10, 10, 10, 10)
Then putting this in my viewDidLoad()
textField.layoutMargins = insets
I ran the program and it still looked like there were no margins. How do I implement margins in a text field in Swift?
Subclass UITextField and implement textRectForBounds:. The simplest strategy is to call super, get the resulting rect, inset it as desired, and return it.
Here's an example result; note that the start and end of the text have considerable white space at the margin (of course the exact amount is up to you):
By creating new UIView with the right(your) values, you can set the padding in UITextField
textField.leftView = UIView(frame: CGRect(x: 30, y: 30, width: 100, height: 100))
textField.leftViewMode = UITextFieldViewMode.Always

Xcode 6 Interface Builder centre multiple labels in middle of view

I'm writing an iOS app which has multiple labels in one view, like shown:
I would like these labels to be in the vertical centre of the view, with the middle of the collection of labels as the centre of the view.
I need to use auto layout for this, as the top label may be multiple lines, or may only be one depending on input, and will change height depending on this. This, along with the top label being a different size, means that I cannot simple have the middle label in the middle, and the others relative to that.
I'm looking for a solution either in code or IB.
EDIT: To clarify, I am looking to centre the middle of multiple labels, like so:
(The vertical middle might be slightly off)
*The image should read vertical middle
This is an old question of mine, but it maintains a fair number of views, and is quite a common use-case. I do not feel the other answer is a very efficient method to achieve this.
The easiest method to centre a collection of views is to place them within a UIView object which is itself centred.
To use the example above, the three UILabels would be within one UIView, with a 0 constraint between the top and bottom labels and the View. The view itself would then be set to be centred vertically.
I don't think there is a way to do this with Auto Layout, but you can code. I'm assuming you've already figured out the spacing of the labels, so I'll just help you center the whole thing.
var o1 = label1.frame.origin
var o4 = label4.frame.origin
var h4 = label4.frame.height
var w4 = label4.frame.width
var hc4 = o4.y + h4
var wc4 = o4.x + w4
var screen = UIScreen.mainScreen().bounds.size.height
var remainingScreen = (screen - (hc4 - o1.y))/2.0
var screenW = UIScreen.mainScreen().bounds.size.width
var remainingScreenW = (screenW - (wc4 - o1.x))/2.0
var moveH = remainingScreen - o1.y
var moveW = remainingScreenW - o1.x
var frame1 = label1.frame
var frame2 = label2.frame
var frame3 = label3.frame
var frame4 = label4.frame
label1.frame = CGRect(x: frame1.origin.x + moveW, y: frame1.origin.y + moveH, width: frame1.width, height: frame1.height)
label2.frame = CGRect(x: frame2.origin.x + moveW, y: frame2.origin.y + moveH, width: frame2.width, height: frame2.height)
label3.frame = CGRect(x: frame3.origin.x + moveW, y: frame3.origin.y + moveH, width: frame3.width, height: frame3.height)
label4.frame = CGRect(x: frame4.origin.x + moveW, y: frame4.origin.y + moveH, width: frame4.width, height: frame4.height)
This whole thing isn't very self explanatory, but I won't talk about what every line does. The overall idea is it finds the distance between the upper left and lower right corner of all the labels (so UL of label1 and LR of label4 [this only works if you've already set the spacing between labels and the width/height of each label]), then it finds the width/height (W/H) of the screen and subtracts the W/H of the label area, divides by two, giving the space between the top of the screen and the labels. Finally, it finds the distance the entire assembly needs to move by comparing the UL corner with where it should be, and combines the amount to be moved with the original location of all the labels.
Note: This code could be heavily condensed, it is just easier to view and read if it is written like this.

Resources