how to programmatically make label larger? - swift

Right now, the edges of the label line up exactly with the end of the text - I want to give the text some padding(?) or space so that you still see some of the background. Is this a constraints issue?
let lblTitle: UILabel = {
let lbl=UILabel()
lbl.text="Try this quiz."
lbl.textColor=UIColor.black
lbl.textAlignment = .justified
lbl.font = UIFont(name: "Gotham", size: 50)
lbl.backgroundColor = UIColor(white: 1, alpha: 0.8)
lbl.adjustsFontSizeToFitWidth = true
lbl.layer.masksToBounds = true
lbl.numberOfLines=0
lbl.sizeToFit()
lbl.layer.cornerRadius = 5
lbl.translatesAutoresizingMaskIntoConstraints=false
lbl.minimumScaleFactor = 0.1
return lbl

You probably should just use constraints (ie put the label in a container and give it space to to container equal to the padding you want, and only give the container 2 position constraints so it infers height and width from the label + padding), but if not its easy enough to do by adding a negative inset to the frame right after you call lbl.sizeToFit. For instance this adds 10 padding points in every direction:
lbl.frame.insetBy(dx: -20, dy: -20)

Related

How do I Fade Label out at end instead of replacing end with "..." if it's too long / How to use GoogleToolboxForMac

I've found a solution with GTMFadeTruncatingLabelTest from GoogleToolboxForMac but don't really understand how to use it and I don't find anything about it
but if you have another solution besides of this one
If you can't help me with GoogleToolboxForMac feel free to suggest other solution
Should kinda look like this at the end:
I am not sure about the GTMFadeTruncatingLabelTest but I can offer an alternative solution.
Steps
Check if the label's text is going to be truncated
If 1 is true, Create a CAGradientLayer that goes from Opaque to Transparent
Apply the gradient layer as a mask to the UILabel
Implementation
If you don't want to read the rest, just grab the code from this repo
I wrapped step 1, 2 and 3 from above in a custom UILabel subclass. The reasoning is explained in the comments.
class FadingLabel: UILabel
{
// Add a property observer for text changes
// as we might not need to fade anymore
override var text: String?
{
didSet
{
// Check if the text needs to be faded
fadeTailIfRequired()
}
}
// Add a property observer for numberOfLines changes
// as only 1 line labels are supported for now
override var numberOfLines: Int
{
didSet
{
// Reset the number of lines to 1
if numberOfLines != 1
{
numberOfLines = 1
}
}
}
override func layoutSubviews()
{
super.layoutSubviews()
// The label's frame might have changed so check
// if the text needs to be faded or not
fadeTailIfRequired()
}
/// The function that handles fading the tail end of the text if the text goes
/// beyond the bounds of the label's width
private func fadeTailIfRequired()
{
// Reset the numberOfLines to 1
numberOfLines = 1
// Prevent processing fading when the library is in the middle of
// processing the string to truncate the ellipsis
if !isTruncatingEllipsis
{
// Check if the label's has it's width set and if the text goes
// beyond it's width plus a margin of safety
if bounds.width > CGFloat.zero && intrinsicContentSize.width > bounds.width + 5
{
// Fade label works better with this setting
allowsDefaultTighteningForTruncation = true
// Initialize and configure a gradient to start at the end of
// the label
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]
gradient.startPoint = CGPoint(x: 0.8, y: 0.5)
gradient.endPoint = CGPoint(x: 0.99, y: 0.5)
// Apply the gradient as a mask to the UILabel
layer.mask = gradient
// Remove ellipsis added as the default UILabel truncation character
removeEllipsis()
// We do not need to go beyond this point
return
}
// If the text has not been truncated, remove the gradient mask
if originalText == text
{
// Remove the layer mask
layer.mask = nil
}
}
}
/// Keep removing 1 character from the label till it no longer needs to truncate
private func removeEllipsis()
{
isTruncatingEllipsis = true
// Keep looping till we do not have the perfect string length
// to fit into the label
while intrinsicContentSize.width > bounds.width
{
// Drop the last character
text = String(text!.dropLast())
}
isTruncatingEllipsis = false
}
}
Then you can use it like a regular UILabel, for example:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let fadingLabelWithLongText = FadingLabel()
view.addSubview(fadingLabelWithLongText)
fadingLabelWithLongText.text = "Fading label with text more than it's bounds can handle"
fadingLabelWithLongText.textColor = .white
fadingLabelWithLongText.frame = CGRect(x: 20, y: 90, width: 250, height: 50)
let regularLabelWithLongText = UILabel()
view.addSubview(regularLabelWithLongText)
regularLabelWithLongText.text = "Regular label with text more than it's bounds can handle"
regularLabelWithLongText.textColor = .white
regularLabelWithLongText.frame = CGRect(x: 20, y: 160, width: 250, height: 50)
let fadingLabelWithShortText = UILabel()
view.addSubview(fadingLabelWithShortText)
fadingLabelWithShortText.text = "Fading label with text that fits"
fadingLabelWithShortText.textColor = .white
fadingLabelWithShortText.frame = CGRect(x: 20, y: 230, width: 250, height: 50)
let regularLabelWithShortText = UILabel()
view.addSubview(regularLabelWithShortText)
regularLabelWithShortText.text = "Regular label with text that fits"
regularLabelWithShortText.textColor = .white
regularLabelWithShortText.frame = CGRect(x: 20, y: 300, width: 250, height: 50)
}
Output
Limitation
This way only supports single line UILabels
Update
Added a function to remove the default truncation method of using ellipsis (three dots) by UILabel with this function.
/// Keep removing 1 character from the label till it no longer needs to truncate
private func removeEllipsis()
{
isTruncatingEllipsis = true
// Keep looping till we do not have the perfect string length
// to fit into the label
while intrinsicContentSize.width > bounds.width
{
// Drop the last character
text = String(text!.dropLast())
}
isTruncatingEllipsis = false
}
This function has been updated in the original code and repo mentioned above.
I Think the easiest way is to use the following code:
titleLabel.adjustsFontSizeToFitWidth = false
titleLabel.lineBreakMode = .byClipping
It automatically fades the last words if applicable and text size has more width than UILabel!
thanks to thi

How can I get frame of superview's frame in swift?

I want to create multiple buttons and position it inside uiview and fit to uiview.(as picture)
I need to get uiview frame to calculate and divide as I need , to set button's width and height depending on device size.
for row in 0 ..< 4 {
for col in 0..<3 {
let numberButton = UIButton()
numberButton.frame = CGRect(x: Int(buttonView.frame.width / 3 - 20) * col, y: row * (320 / 4), width: Int(buttonView.frame.width) / 3, height: Int(buttonView.frame.height) / 4)
numberButton.setTitle("button", for: .normal)
numberButton.titleLabel?.font = numberButton.titleLabel?.font.withSize(30)
numberButton.setTitleColor(UIColor.black)
buttonView.addSubview(numberButton)
}
}
I tried like code above, but buttonView.frame.width returns nil.
How can I calculate this view's frame?
You can use UIStackViews to achieve this grid layout. This way, you don't have to calculate the frames of each button. Doing so is bad practice anyway. You should instead use AutoLayout constraints to layout your views. Here's a tutorial to get you started.
Anyway, here's how you would use UIStackViews to create a grid of buttons:
// here I hardcoded the frame of the button view, but in reality you should add
// AutoLayout constraints to it to specify its frame
let buttonView = UIStackView(frame: CGRect(x: 0, y: 0, width: 600, height: 320))
buttonView.alignment = .fill
buttonView.axis = .vertical
buttonView.distribution = .fillEqually
buttonView.spacing = 20 // this is the spacing between each row of buttons
for _ in 0..<4 {
var buttons = [UIButton]()
for _ in 0..<3 {
let numberButton = UIButton(type: .system)
numberButton.setTitle("button", for: .normal)
numberButton.titleLabel?.font = numberButton.titleLabel?.font.withSize(30)
numberButton.setTitleColor(UIColor.black, for: .normal)
// customise your button more if you want...
buttons.append(numberButton)
}
let horizontalStackView = UIStackView(arrangedSubviews: buttons)
horizontalStackView.alignment = .fill
horizontalStackView.axis = .horizontal
horizontalStackView.distribution = .fillEqually
horizontalStackView.spacing = 20 // this is the spacing between each column of buttons
buttonView.addArrangedSubview(horizontalStackView)
}
Result from playground quick look:

UILabel SizetoFit After Specifying Number of Lines?

I would like to create a UILabel programmatically after specifying the number of lines. I am using Swift. I have the following code:
let newLabel = UILabel()
newLabel.text = "Hello There"
newLabel.font = UIFont.systemFont(ofSize: 16)
newLabel.numberOfLines = 2
newLabel.lineBreakMode = .byWordWrapping
newLabel.sizeToFit()
newLabel.frame.origin.x = 100
newLabel.frame.origin.y = 500
view.addSubview(newLabel)
The problem is that the label has the text on one line, rather than two lines. I have to use sizeToFit because the fontSize is actually dynamic (it is not always 16). How can I make sure that the label is 2 lines? Thanks.
You are not specifying exact frame of your UILabel.so your view just get any width it wants.you can use this for getting result:
newLabel.frame = CGRect(x: 100, y: 500, width: newLabel.frame.width - 1, height: newLabel.frame.height * 2)
But that's not very good and I suggest using AutoLayout
There are many things you can do to break line.
1) You can use "\n" in between the words. But not sure about your usecase and whether this make sense.
2) Thought not recommended you can define the width of the label.
3) Use numberOfLines = 0, if you can go to more than 2 lines. However, if you only want 2 lines then give numberOfLines = 2.
Also use Constraints something like below and not frame:
private let newLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello There"
label.font = UIFont.systemFont(ofSize: 16)
label.numberOfLines = 0
return label
}()
view.addSubview(newLabel)
NSLayoutConstraint.activate([
newLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 500),
newLabel.leadingAnchor.constraint(equalTo:view.leadingAnchor, constant: 100),
newLabel.widthAnchor.constraint(equalToConstant: 70)
])

swift textfield bottom line max width

I wrote the below code to make the textfield only have bottom line,
the result show as the below image (the bottom line is not long enough to the right edge.)
func setBottomBorder() {
self.borderStyle = .none
self.layer.backgroundColor = UIColor.clear.cgColor
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes: [NSAttributedStringKey.foregroundColor: UIColor.white])
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.white.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
As per My suggestion instead of progrmatically.
you should set it using StoryBoard:
A textfield with Hidden Border.
Below that you can take a label with white background and it is done.
Note:- Do not forget to give correct constraints to both so that it looks good

How to add label to SCNNode?

I’m trying to add a label to an SCNNode. So far I’ve managed to set a SKLabelNode as the material for the SCNNode. It sort of works, I can the SCNNode becomes the background colour of the SKLabelNode but I can’t see the text. Sometime I can see a red haze (the text colour is red) but no readable text.
I also tried setting the material as a UIView and adding a UiLabel as a sub view. Again it sets as I can the whole SCNNode becomes the background colour of the UiLabel but I can’t see any text.
var block = SCNNode()
var spriteScene = SKScene()
var Lbl = SKLabelNode()
lbl.text = “Hello World”
lbl.color = blue (playground colour literal)
lbl. = 2 //I tried various numbers
lbl.fontColor = SKColor.red
spriteScene.addChild(lbl)
I got it after some hit and trial. I had to try different values before I got these size, scale and rotation to display the label as I want.
note: My node here is SCNPlane with 0.3 width and 0.2 height, so size of SKScene and rectangle and position of label are hard coded accordingly.
func addLabel(text: String){
let sk = SKScene(size: CGSize(width: 3000, height: 2000))
sk.backgroundColor = UIColor.clear
let rectangle = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 3000, height: 2000), cornerRadius: 10)
rectangle.fillColor = UIColor.black
rectangle.strokeColor = UIColor.white
rectangle.lineWidth = 5
rectangle.alpha = 0.5
let lbl = SKLabelNode(text: text)
lbl.fontSize = 160
lbl.numberOfLines = 0
lbl.fontColor = UIColor.white
lbl.fontName = "Helvetica-Bold"
lbl.position = CGPoint(x:1500,y:1000)
lbl.preferredMaxLayoutWidth = 2900
lbl.horizontalAlignmentMode = .center
lbl.verticalAlignmentMode = .center
lbl.zRotation = .pi
sk.addChild(rectangle)
sk.addChild(lbl)
let material = SCNMaterial()
material.isDoubleSided = true
material.diffuse.contents = sk
node.geometry?.materials = [material]
node.geometry?.firstMaterial?.diffuse.contentsTransform = SCNMatrix4MakeScale(Float(1), Float(1), 1)
node.geometry?.firstMaterial?.diffuse.wrapS = .repeat
node.geometry?.firstMaterial?.diffuse.wrapS = .repeat
}