Moving Labels within External Screen - ios

I'm wanting to move my labels wherever I want on my external display. (eg. displayRunsLabel at the top and centered or perhaps 40px from right and top)
I have my labels showing on the screen. I'm currently using text alignment.
What's the best way to do this?
Here's my external display:
ExternalDisplayScreenshot
Here's the code:
// CUSTOMISE VIEW
secondScreenView.backgroundColor = UIColor.black
displayWicketLabel.textAlignment = NSTextAlignment.left
displayWicketLabel.font = UIFont(name: "Arial-BoldMT", size: 200.0)
displayWicketLabel.textColor = UIColor.white
displayWicketLabel.frame = secondScreenView.bounds
secondScreenView.addSubview(displayWicketLabel)
displayRunsLabel.textAlignment = NSTextAlignment.center
displayRunsLabel.font = UIFont(name: "Arial-BoldMT", size: 200.0)
displayRunsLabel.textColor = UIColor.white
displayRunsLabel.frame = secondScreenView.bounds
secondScreenView.addSubview(displayRunsLabel)
displayOversLabel.textAlignment = NSTextAlignment.right
displayOversLabel.font = UIFont(name: "Arial-BoldMT", size: 200.0)
displayOversLabel.textColor = UIColor.white
displayOversLabel.frame = secondScreenView.bounds
secondScreenView.addSubview(displayOversLabel)
}

The position of a label or any other UIView is the origin of its frame, which is a CGPoint representing its top left corner.
So, you are currently saying:
displayWicketLabel.frame = secondScreenView.bounds
displayRunsLabel.frame = secondScreenView.bounds
displayOversLabel.frame = secondScreenView.bounds
Well, that's pretty silly, because you have given all three labels the same frame! Thus they overlay one another, and (as you rightly say) you can only read them by giving them different text alignments.
Instead, give them each a different frame — one that puts the label where you want it. For example:
displayWicketLabel.sizeToFit()
displayWicketLabel.frame.origin = CGPoint(x:30, y:20) // or whatever
displayRunsLabel.sizeToFit()
displayRunsLabel.frame.origin = CGPoint(x:30, y:100) // or whatever
displayOversLabel.sizeToFit()
displayOversLabel.frame.origin = CGPoint(x:30, y:200) // or whatever
Now, having said all that, a clear flaw in the code I just showed you is that we have hard-coded the positions arbitrarily, without taking account of what the size of the window is (the external display). For that reason, a much better way would be for you to learn about auto layout, which will let you lay the three labels out in nice positions automatically based on the size of the window.

Related

How to wrap text in Swift 5

Brand new to Xcode and Swift, but I'm trying to have a headline and subheading for a news type app I'm prototyping out. I can't get the text to wrap, leaving out most of the headline and brief summary. I need it to say
Unity with purpose. Amanda Gorman and Michelle Obama Discuss Art Identity and Optimism
Amanda Gorman captivated the world when she read her poem “The Hill We
Climb” at President Joe Biden and Vice President Kamala Harris’ Jan.
20 Inauguration ceremony.
All I get is:
Here is the code generating the labels, this is the headline but they are basically the same:
private let headlineLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.textColor = .white
label.font = UIFont.preferredFont(forTextStyle: .largeTitle) //Scales font automatically
label.adjustsFontForContentSizeCategory = true //Adjusts font sized based on settings user has
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
return label
}()
I've tried setting numberOfLines = 0 as well as label.lineBreakMode = .byWordWrapping and neither seem to work.
How can I get the entire string for both the headline and subheading to be displayed?
Thanks!
The properties that you applied to your UILabel to allow wrapping looks correct, but
without seeing more of the code it's hard to tell. So I'm betting it's your horizontal layout constraints (if any). If you did not set an explicit frame to your view. Then ensure you're setting+activating vertical and horizontal constraints for your view.
// Important step to enable contraints
label.translatesAutoresizingMaskIntoConstraints = false
....
// Important that you add your view to the view hierarchy first before activating
// constraints. If not this will produce a runtime error.
self.view.addSubview(label)
....
NSLayoutConstraint.activate([
label.leadingAnchor.contraint(equalTo: self.view.leadingAnchor)
label.trailingAnchor.contraint(equalTo: self.view.trailingAnchor)
label.topAnchor.contraint(equalTo: self.view.topAnchor)
])
The .activate(:) is another important step and common pitfall. Ensure you're activating your contraints!
leadingAnchor is attaching the left side of your label's frame to the left side of your parent view.
trailingAnchor is doing the same thing, but for the right side.
Then finally we pin the label to the top of the parent view. You may want use .contraint(equalTo: .., constant: ..) to push your label down a little bit as well.
So, in the next update loop and when your parent view lays out the subviews. Your label's frame will be calculated based on your activated constraints. So in this case your label's frame will look something like this (x:0, y:0, width: 300, height: *height is intrinsic here) This is assuming a parent frame of (x: 0, y: 0, width: 300, height: 600)
You mentioned you're new to iOS, so I hope this info helps you in some way.

Is it possible to set the alignment of segmented Control titles to the left?

I have been looking around for a way to set the alignment of the segmented control titles to the left but I don't seem to be able to achieve what I want.
I have created this little function to change the frame of the subviews of the segment control.
It works at first.
func modifyFrameOfSegment() {
for segment in segmentedControl.subviews {
guard segment.subviews.isNotEmpty else { return }
segment.contentMode = .left
for label in segment.subviews where label is UILabel {
label.frame = CGRect(x: 0, y: label.frame.origin.y, width: label.frame.size.width, height: label.frame.size.height)
(label as! UILabel).textAlignment = .left
}
}
}
But everytime I select a new segment it resets the frames of all the subviews and center align all the titles again.
Is there a way to achieve a permanent left alignment for the segment titles in a segmented control?
Any tips or advice would be greatly appreciated.
Thank you for your time.
Let's use this method
self.segmentedControl.setContentPositionAdjustment(UIOffset(horizontal: -20, vertical: 0), forSegmentType: .left, barMetrics: .default)
And you can do what you want (Of course, you can change the horizontal & vertical value by your needs). Here is the result:
Update:
There's apparently no way to set the alignment of the items, but you can fake it by adjusting the position of each individual item using setContentOffset(_ offset: CGSize, forSegmentAt segment: Int). Here's a kludgy example:
class LeftSegmentedControl: UISegmentedControl {
var margin : CGFloat = 10
override func layoutSubviews() {
super.layoutSubviews()
leftJustifyItems()
}
func leftJustifyItems() {
let fontAttributes = titleTextAttributes(for: .normal)
let segments = numberOfSegments - 1
let controlWidth = frame.size.width
let segmentWidth = controlWidth / CGFloat(numberOfSegments)
for segment in 0...segments {
let title = titleForSegment(at: segment)
setWidth(segmentWidth, forSegmentAt: segment)
if let t = title {
let titleSize = t.size(withAttributes: fontAttributes)
let offset = (segmentWidth - titleSize.width) / 2 - margin
self.setContentOffset(CGSize(width: -offset, height: 0), forSegmentAt: segment)
}
}
}
}
Here's what it looks like:
There are a few caveats:
This version sets the segments to all have equal width, which might not be what you want.
I used a fixed left margin of 10px because it seems unlikely that you'd want to vary that, but you can obviously change it or make it a settable property.
Just because you can do this doesn't mean you should. Personally, I don't think it looks great, and it suffers in the usability department too. Users expect segmented control items to be centered, and left-justifying the items will make it harder for them to know where to tap to hit the segment. That seems particularly true for short items like the one labelled "3rd" in the example. It's not terrible, it just seems a little weird.
Original answer:
UIControl (of which UISegmentedControl is a subclass) has a contentHorizontalAlignment property that's supposed to tell the control to align its content a certain way, so the logical thing to do would be to set it like this:
let segmented = UISegmentedControl(items: ["Yes", "No", "Maybe"])
segmented.frame = CGRect(x:75, y:250, width:250, height:35)
segmented.contentHorizontalAlignment = .left
But that doesn't work — you still get the labels centered. If you've got a compelling use case for left-aligned segments, you should send the request to Apple.
One way you could work around this problem is to render your labels into images and then use the images as the segment labels instead of plain strings. Starting from the code in How to convert a UIView to an image, you could easily subclass UISegmentedControl to create images from the item strings.

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.

Why aren't the backgrounds within these UITextViews clear?

I am attempting to display individual characters in the exact positions that they would appear if displayed as a single string with kerning. The problem is that the characters' bounding boxes seem to be opaque, so that each newly added character covers some of the prior one. Where kerning is greater (e.g., in the combination "ToT"), the problem is obvious:
My setup is something like this: I have an SKView embedded in a container view. In an extension of the SKView's view controller, the following are set within a function in this order:
skView.allowsTransparency = true
scene.backgroundColor = .clear
charAttr – [NSAttributedStringKey.backgroundColor: UIColor.clear]
textView.isOpaque = false
textView.backgroundColor = UIColor.clear
Each UITextView is added successively as a subview to the view (which is an SKView).
I've looked all over my code for some clue as to what could be making the character's bounding boxes seem opaque, but I haven't found anything. The sad thing is that I solved this problem sometime last year, but don't remember what I did and don't have the code anymore.
Any insights or ideas would be appreciated.
After achieving the sought-after effect in a playground, I pasted this simple code into the extension where the original code was. It still worked, so I made it as close to identical to the original as possible, until it also exhibited the problem.
The SKView and extension aspects were irrelevant. The problem lies with how UITextView frames property deals with character widths. Here's the relevant code:
// charInfoArray contains (among other things) an attributed character, its origin,
// its size, and info about whether or not it should be displayed
// For each char, the origin and size info were derived from...
let currentCharRect = layoutManager.boundingRect(forGlyphRange: currentCharRange, in: textContainer)
// To display each (attributed) char of "ToT\n" . . .
for (index, charInfo) in charInfoArray.enumerated() {
guard charInfo.displayed == true else { continue }
let textView = UITextView()
textView.backgroundColor = UIColor.clear
textView.attributedText = charInfo.attrChar
textView.textContainerInset = UIEdgeInsets.zero
// let width = charInfo.size!.width
let width = 30 // arbitrary width
// Using the automatically generated charInfo.size!.width here
// appears to make the text view's background opaque!
textView.frame = CGRect(origin: charInfo.origin!,
size: CGSize(width: width, height: charInfo.size!.height))
textView.frame = textView.frame.offsetBy(dx: 0.0, dy: offsetToCenterY)
textView.textContainer.lineFragmentPadding = CGFloat(0.0)
textView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.2)
textView.textColor = UIColor.black
view.addSubview(textView)
print(charInfo.size!.width)
}
Setting the width to width = charInfo.size!.width seems to make the text view's background opaque! This may be caused by the unusually large width assigned to newline char at the end of the sequence. To eliminate the problem, I'll have to deal with newline chars in another way. In any case, I have no idea why this causes the text view's background to turn opaque, but it does. Setting the width to an arbitrary value of, say, 30 eliminates problem.
Here are the results of using automatically generated and manually set widths, respectively:
The translucent red areas (on yellow backgrounds) show the bounding rectangles for each of the characters, including the newline character.

Dynamically adjust width of UILabel in Swift 3

I'm trying to get my UILabel to get wider (along with its border and background color) as the content gets more - and then less when the content is reduced.
Where do I go to get started, I've looked at the Attributes Inspectors and it looks like this can only be done with code (which I'm fine with).
I thought adding two labels in a horizontal stack would do the trick, but it doesn't update in real-time (it will update the label only on launch).
Try using :
myLabel.sizeToFit()
on your label.This should update the label's frame to fit the content.
let label:UILabel = UILabel()
label.textColor=UIColor.black
label.font = UIFont(name: "Halvetica", size: 17)
label.numberOfLines = 1
label.text = "your string"
label.sizeToFit()
label.frame = CGRect(x: 5, y: imageView.frame.height+10, width: label.frame.width, height:label.frame.height)

Resources