I'm experimenting with layout anchors, and I'm trying to replicate the following illustration: basically an outer view with an inner view. The inner view is anchored with constants to the outer view's bottom anchor and the leading anchor. This much is done.
However, I would then like to place a label inside the inner view, and be able to center the label/s in that view. Is this possible?
Ideally I could then go an alter the constants of the view later on, and the labels would still be correctly positioned.
Just some pointers in the right direction would be useful.
First create the view.
let innerView = UIView()
Then the labels.
let label1 = UILabel()
let label2 = UILabel()
Place the inner view inside the main view
view.addSubview(innerView)
After you add the innerView to the view.
You can provide a position and size to the inner view.
innerView.frame = CGRect(x: (self.view.frame.size.width) - (self.view.frame.size.width) + 10, y: (self.view.frame.size.height) - (self.view.frame.size.height) - 150, width: 500, height: 500)
Then add in the labels into the inner view
innerView.addSubview(label1)
innerView.addSubview(label2)
Then you can apply constraints to the labels inside the inner view and give it size
label1.frame = CGRect(x:100, y: 100, width: 200, height: 20)
label2.frame = CGRect(x:100, y: 100, width: 200, height: 20)
The quick pointer is to use UIStackView with
stack.addArrangedSubview(lbl)
distribution = fill
alignment = center
axis = vertical
You may also need to set lbl's alignment = center
Related
I'm attempting to include a series of UITextFields, with corresponding UILabels, in a vertical UIStackView. I'm nesting each label/field pair in a horizontal UIStackView, and the width of the labels is hard-coded to the widest intrinsic width of all the labels, so that they line up nicely.
It very nearly works, but the horizontal stack views are not spaced evenly as expected (spacing = 5) and are bunched up and overlapping.
HOWVER, if I delete the line hFieldStack.alignment = .lastBaseline, then it all works perfectly.
I'm OK with deleting that line, as it looks fine. But I'm just curious as to why spacing in the outside stack view doesn't work when using lastBaseline alignment within the nested stack view.
stackView.axis = .vertical
stackView.spacing = 5
stackView.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0)
stackView.isLayoutMarginsRelativeArrangement = true
if let titleLabel = titleLabel {
stackView.addArrangedSubview(titleLabel)
}
if let messageLabel = messageLabel {
stackView.addArrangedSubview(messageLabel)
}
if textFields.count == fieldLabels.count {
for i in 0..<textFields.count {
fieldLabels[i].widthAnchor.constraint(equalToConstant: 80).isActive = true
let hFieldStack = UIStackView()
hFieldStack.translatesAutoresizingMaskIntoConstraints = false
hFieldStack.axis = .horizontal
hFieldStack.alignment = .lastBaseline
hFieldStack.spacing = 5
hFieldStack.layoutMargins = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
hFieldStack.isLayoutMarginsRelativeArrangement = true
hFieldStack.addArrangedSubview(fieldLabels[i])
hFieldStack.addArrangedSubview(textFields[i])
stackView.addArrangedSubview(hFieldStack)
}
} else {
logger.error("Field count doesn't match label count")
}
Curious...
It appears that, when setting the horizontal stack view's Alignment to .lastBaseline (also happens with .firstBaseline), Auto-layout is using the text field's _UITextFieldCanvasView to calculate the layout.
So, with a rounded-rect border, the actual frame of the text field is taller than the _UITextFieldCanvasView.
We can confirm this by inspecting the view hierarchy:
The labels are nicely baseline aligned with the text rendered in the _UITextFieldCanvasView... but the frames are obviously extending outside the stack view's framing.
While the docs do list .lastBaseline as. valid Alignment constant, it's somewhat telling that, in Storyboard / Interface Builder, the only Alignment options are Fill, Leading, Center and Trailing.
I've come across several actual Bugs related to StackViews --- so I think I would (personally) add this one to the list.
I'm stuck at the Consolidation IV challenge from hackingwithswift.com.
Right now I'm trying to create a hangman game. I thought to place placeholder labels based on the length of the answer word. These placeholder labels would be placed inside a frame, which then would be placed in the center of the main view.
Unfortunately, the leading edge of the frame is placed centered. In my opinion, this is not a problem of constraints, but rather a problem of me creating the frame wrong.
My current code is
import UIKit
class ViewController: UIViewController {
var answer: String!
override func viewDidLoad() {
super.viewDidLoad()
// MARK: - declare all the labels here
let letterView = UIView()
letterView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(letterView)
// MARK: - set constraints to all labels, buttons etc.
NSLayoutConstraint.activate([
letterView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
letterView.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor)
])
// MARK: - populate the letterView
// set the size of the placeholder
answer = "Atmosphäre"
let height = 60
let width = 25
// var width: Int
for placeholder in 0..<answer.count {
// create new label and give it a big font size
let placeholderLabel = UILabel()
placeholderLabel.text = "_"
placeholderLabel.font = UIFont.systemFont(ofSize: 36)
// calculate frame of this label
let frame = CGRect(x: placeholder * width, y: height, width: width, height: height)
placeholderLabel.frame = frame
// add label to the label view
letterView.addSubview(placeholderLabel)
}
}
}
The simulator screen looks just like this:
I already searched for answers on stackoverflow, but wasn't successful. I think I don't know what I'm exactly looking for.
The main problem, is that the letterView has no size, because no width or height constraints are applied to it.
To fix your code make the letterView big enough to contain the labels you've added as subviews by adding height and width constraints after the for loop:
for placeholder in 0..<answer.count {
...
}
NSLayoutConstraint.activate([
letterView.widthAnchor.constraint(equalToConstant: CGFloat(width * answer.count)),
letterView.heightAnchor.constraint(equalToConstant: CGFloat(height))
])
I'm not sure if you've covered this in your course yet, but a better way to go about this (which would take much less code), is to use a UIStackView as your letterView instead.
An extra thing to consider:
If you give the letterView a background color, you'll see that the labels are actually aligned outside of its bounds:
That's because you're setting each label's y position to be height, when it should probably be zero:
let frame = CGRect(x: placeholder * width, y: 0, width: width, height: height)
Correcting this places the labels within the bounds of the letterView:
i have created a subview with a lot sliders in it
var sliderArea = UIView()
sliderArea = UIView(frame: CGRect(x: 300, y: 400, width: 500, height: 100)
view.addSubview(sliderArea)
mySlider1 = setUpSlider(sliderNr: 1, ypos: 30)
mySlider2 = setUpSlider(sliderNr: 2, ypos: 30)
mySlider3 = setUpSlider(sliderNr: 3, ypos: 30)
sliderArea.addSubview(mySlider1)
sliderArea.addSubview(mySlider2)
sliderArea.addSubview(mySlider3)
i have a lot of subviews similar to the "sliderArea" to be able to change my sliders quickly while the layout adopts automatically
now i need to know where the absolute position of each slider is to place buttons on top of it. i need to have all this buttons inside an extra view on top of it all. any ideas? thank you
You should convert your slider frame to window's coordinate system using this method of CGRect:
https://developer.apple.com/documentation/uikit/uiview/1622504-convert
Just pass nil as second parameter and it will return you an absolute frame.
What I want to do is create a new UILabel programmatically every time a certain action occurs in my code. I know the x, y, and height that I want to give the label, but I don't want to give it a set width. I want to constrain the sides so that the UILabel width is equal to the width of the screen, and so that the label width will change if the orientation is flipped.
I have considered using:
CGRect(x:, y:, width:, height:)
However, I would have to give it a set width if I use this, so I don't think it will work.
I also tried using:
CGPoint(x:, y:)
Then setting leading, trailing and height anchors, however, this doesn't seem to work either as even though it does compile, I get an error when I try creating a new UILabel.
I'm kind of new to programming in Swift so I'm not sure if there is an obvious fix to this.
Like you said, we already have x, y and height available for the label, i.e.
let x: CGFloat = 0
let y: CGFloat = 0
let height: CGFloat = 50
Let's create a label using the above details. Also set the width of the label as UIScreen.main.bounds.width as per your requirement.
let label = UILabel(frame: CGRect(x: x, y: y, width: UIScreen.main.bounds.width, height: height))
label.text = "This is a sample text"
Don't forget to set label's translatesAutoresizingMaskIntoConstraints as false and add it to whatever subview you want.
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
Add the relevant constraints of label with its superview - top, bottom, leading, height. You can definitely add bottom constraint if required. That totally depends upon your UI.
I'm adding the label to the top of the viewController's view.
NSLayoutConstraint.activate([
label.heightAnchor.constraint(equalToConstant: height),
view.topAnchor.constraint(equalTo: label.topAnchor),
view.leadingAnchor.constraint(equalTo: label.leadingAnchor),
view.trailingAnchor.constraint(equalTo: label.trailingAnchor)
])
You can use following code to create UILabel programmatically.
private let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.text = "Hello World"
return label
}()
Then inside your viewDidLoad()
addSubview(label)
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: label.topAnchor),
bottomAnchor.constraint(equalTo: label.bottomAnchor),
leadingAnchor.constraint(equalTo: label.leadingAnchor),
trailingAnchor.constraint(equalTo: label.trailingAnchor)
])
Hello I am working on textview. I have a set content for it with attributed text. Based on which screen it will be played the textview height should be adjusted to fit screen width, I am using the frame as
mytextView.frame = CGrect(x: 0, y: 800, width: view.bounds.width, height: .greatestFiniteMagnitude)
the textview height is adjusted accordingly but I have two issues
1) textview always place at the top of the view and mixed up with other labels. I want the x:0 & y:800 for the textView
2) how to determine the modified height of the textview so as to set the constraint for scrollview (I don't want the textview to scroll, i.e. a fixed height to its contentsize)
please let me know why the frame x and y are not working?
thanks
thanks for the help joe, I did find an answer,
I changed in the viewdidload as
mytextView.frame = CGrect(x: 0, y: 800, width: view.bounds.width, height: 200)
and mytextView.isScrollenable = false
so that viewcontroller calculate what should be the actual height of the mytextView
and in
viewdidlayoutsubviews
i used contentsize to adjust the height of mytestView, also I used this height to determine what should be the scrolling height scrollView