UIView setting constraints - ios

I am having trouble creating a UIView instance programmatically. I have a function that returns the view so that I can use it in an NSAttachment inside a UITextView.
This is what I am trying to achieve:
This is what I am getting in the simulator:
Code Below:
let fullView = UIView()
let firstButton = UIButton()
let secondButton = UIButton()
let thirdTextView = UITextView()
fullView.frame = CGRect(x: 0, y: 0, width: textView.frame.width, height: 90)
fullView.backgroundColor = UIColor(red:0.82, green:0.83, blue:0.85, alpha:1.0)
firstButton.setTitle(text1, for: .normal)
firstButton.setTitleColor(.black, for: .normal)
firstButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
firstButton.contentHorizontalAlignment = .left
secondButton.setTitle("Button2", for: .normal)
secondButton.setTitleColor(.black, for: .normal)
secondButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
secondButton.contentHorizontalAlignment = .right
thirdTextView.text = text2
let descriptionBarStackView = UIStackView()
descriptionBarStackView.axis = .horizontal
descriptionBarStackView.alignment = .fill
descriptionBarStackView.distribution = .fillProportionally
descriptionBarStackView.addArrangedSubview(firstButton)
descriptionBarStackView.addArrangedSubview(secondButton)
let viewWithStackViews = UIStackView()
viewWithStackViews.axis = .vertical
viewWithStackViews.alignment = .fill // .leading .firstBaseline .center .trailing .lastBaseline
viewWithStackViews.distribution = .fillEqually
viewWithStackViews.addArrangedSubview(descriptionBarStackView)
viewWithStackViews.addArrangedSubview(thirdTextView)
fullView.addSubview(viewWithStackViews)
descriptionBarStackView.translatesAutoresizingMaskIntoConstraints = false
thirdTextView.translatesAutoresizingMaskIntoConstraints = false
viewWithStackViews.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
viewWithStackViews.topAnchor.constraint(equalTo: fullView.topAnchor, constant: 5),
viewWithStackViews.leadingAnchor.constraint(equalTo: fullView.leadingAnchor, constant: 5),
viewWithStackViews.trailingAnchor.constraint(equalTo: fullView.trailingAnchor, constant: 5),
viewWithStackViews.bottomAnchor.constraint(equalTo: fullView.bottomAnchor, constant: 5),
])
Edit:

Check this updated method
func customView(){
let fullView = UIView()
let firstButton = UIButton()
let secondButton = UIButton()
let thirdTextView = UITextView()
fullView.frame = CGRect(x: 0, y: 0, width: mainView.frame.width, height: mainView.frame.height)
fullView.backgroundColor = UIColor(red:0.82, green:0.83, blue:0.85, alpha:1.0)
firstButton.setTitle("Button1", for: .normal)
firstButton.setTitleColor(.black, for: .normal)
firstButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
firstButton.contentHorizontalAlignment = .left
firstButton.backgroundColor = .clear
secondButton.setTitle("Button2", for: .normal)
secondButton.setTitleColor(.black, for: .normal)
secondButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
secondButton.contentHorizontalAlignment = .right
secondButton.backgroundColor = .clear
thirdTextView.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
thirdTextView.backgroundColor = .clear
let descriptionBarStackView = UIStackView()
descriptionBarStackView.axis = .horizontal
descriptionBarStackView.alignment = .fill
descriptionBarStackView.distribution = .fillProportionally
descriptionBarStackView.isLayoutMarginsRelativeArrangement = true
descriptionBarStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5)
descriptionBarStackView.addArrangedSubview(firstButton)
descriptionBarStackView.addArrangedSubview(secondButton)
let viewWithStackViews = UIStackView()
viewWithStackViews.axis = .vertical
viewWithStackViews.alignment = .fill // .leading .firstBaseline .center .trailing .lastBaseline
viewWithStackViews.distribution = .fill
viewWithStackViews.addArrangedSubview(descriptionBarStackView)
viewWithStackViews.addArrangedSubview(thirdTextView)
fullView.addSubview(viewWithStackViews)
descriptionBarStackView.translatesAutoresizingMaskIntoConstraints = false
thirdTextView.translatesAutoresizingMaskIntoConstraints = false
viewWithStackViews.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: descriptionBarStackView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint.activate([
viewWithStackViews.topAnchor.constraint(equalTo: fullView.topAnchor, constant: 0),
viewWithStackViews.leadingAnchor.constraint(equalTo: fullView.leadingAnchor, constant: 0),
viewWithStackViews.trailingAnchor.constraint(equalTo: fullView.trailingAnchor, constant: 0),
viewWithStackViews.bottomAnchor.constraint(equalTo: fullView.bottomAnchor, constant: 0),
])
fullView.layer.cornerRadius = 5
mainView.addSubview(fullView)
}
here i have listed changes
mainView.frame.height full height based on main text view
firstButton.backgroundColor = .clear
secondButton.backgroundColor = .clear
thirdTextView.backgroundColor = .clear
margin for button
descriptionBarStackView.isLayoutMarginsRelativeArrangement = true
descriptionBarStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5)
NSLayoutConstraint constraint zero to all in full view
fullView.layer.cornerRadius = 5
thats it here is result..

Tried to preserve your layout structure
class VisualTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let fullView = UIView()
fullView.backgroundColor = UIColor(red:0.82, green:0.83, blue:0.85, alpha:1.0)
fullView.translatesAutoresizingMaskIntoConstraints = false
let firstButton = UIButton()
firstButton.translatesAutoresizingMaskIntoConstraints = false
let secondButton = UIButton()
secondButton.translatesAutoresizingMaskIntoConstraints = false
let thirdTextView = UITextView()
thirdTextView.translatesAutoresizingMaskIntoConstraints = false
thirdTextView.text = "lorem ipsumlorem ipsumlorem ipsumlorem ipsumlorem ipsumlorem ipsumlorem ipsumlorem ipsumlorem ipsum"
let text1 = "Button1"
firstButton.setTitle(text1, for: .normal)
firstButton.setTitleColor(.black, for: .normal)
firstButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
firstButton.contentHorizontalAlignment = .left
secondButton.setTitle("Button2", for: .normal)
secondButton.setTitleColor(.black, for: .normal)
secondButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
secondButton.contentHorizontalAlignment = .right
let descriptionBarStackView = UIStackView(arrangedSubviews: [firstButton, UIView() ,secondButton])
descriptionBarStackView.translatesAutoresizingMaskIntoConstraints = false
descriptionBarStackView.axis = .horizontal
descriptionBarStackView.alignment = .center
let viewWithStackViews = UIStackView(arrangedSubviews: [descriptionBarStackView, thirdTextView])
viewWithStackViews.translatesAutoresizingMaskIntoConstraints = false
viewWithStackViews.axis = .vertical
viewWithStackViews.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
viewWithStackViews.isLayoutMarginsRelativeArrangement = true
fullView.addSubview(viewWithStackViews)
fullView.leadingAnchor.constraint(equalTo: viewWithStackViews.leadingAnchor).isActive = true
fullView.trailingAnchor.constraint(equalTo: viewWithStackViews.trailingAnchor).isActive = true
fullView.topAnchor.constraint(equalTo: viewWithStackViews.topAnchor).isActive = true
fullView.bottomAnchor.constraint(equalTo: viewWithStackViews.bottomAnchor).isActive = true
view.addSubview(fullView)
fullView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -10).isActive = true
fullView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
fullView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
thirdTextView.isScrollEnabled = false
thirdTextView.backgroundColor = .clear
}
}

You are setting
viewWithStackViews.distribution = .fillEqually
Because of .fillEqually the StackView will allocate equal height to the subviews. Thats why you are seeing buttons in half of viewWithStackViews and thirdTextView in remaining half.
So change it to:
viewWithStackViews.distribution = .fillProportionally
One more thing: your thirdTextView is not getting its height according to its content.
Try this to allow you thirdTextView to get height automatically. In case the text might be too long, set scrolledEnabled to true and set height contraint for the textView.
thirdTextView.translatesAutoresizingMaskIntoConstraints = true
thirdTextView.sizeToFit()
thirdTextView.scrollEnabled = false

Related

Set Constraints to Labels So That One is At Leading Side and The Other at Trailing Side in Swift

I am programmatically adding a label and a button in Swift. I want the label to be at leading side and the button at trailing side. This is what I did so far:
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: 50))
view.clipsToBounds = true
view.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
button.frame = CGRect.init(x: 100, y: 0, width: view.frame.width-25, height: view.frame.height-25)
label.frame = CGRect.init(x: 10 , y: 0, width: view.frame.width-10, height: view.frame.height-10)
view.addSubview(label)
view.addSubview(button)
And this results in:
My desired result is:
I want see all to be at trailing side and the top-rated label at the leading side. How can I achieve it in Swift?
Question is a little unclear, I think you are asking how to achieve the same layout with constraints instead of using frames?
Here is a rough sample of how to do it, you might need to tweak the constants:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let view = UIView(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: 50))
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
view.addSubview(label)
view.addSubview(button)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
label.trailingAnchor.constraint(equalTo: button.leadingAnchor, constant: 16),
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 16),
button.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16)
])
self.view.addSubview(view)
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 21, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
let stack = UIStackView(arrangedSubviews: [label, button])
stack.axis = .horizontal
stack.distribution = .fill
stack.alignment = .center
stack.spacing = 16
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
let padding: CGFloat = 16
let viewHeight: CGFloat = 50
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: padding),
stack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: padding),
stack.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 1-padding),
stack.heightAnchor.constraint(equalToConstant: viewHeight)
])
}

Swift: Programmatic Autolayout broken by UNavigationController

I have inherited a UIKit app where my predecessor has written all the interface code by hand. I have a view controller, a simple login screen that works just fine, but when I add it to a UINavigationController it is oddly stretched. This doesn't make a lot of sense to me.
I feel like I must be missing some simple flag, or what do I need to do to make the Navcontroller play nice with this programmatic autolayout (which is hopefully my last ever)
// this a view controller extension
func apply(constraints: [NSLayoutConstraint]) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(constraints)
}
There's my handy dandy, seemingly sane utility for applying constraints. As an example, the nice little oval thing making my password textfield looks nice has these constraints set up.
// these constraints are being setup in the view controller
emailCapsuleView.apply(constraints: [
emailCapsuleView.topAnchor.constraint(equalTo: subBigTitle.bottomAnchor, constant: 24.0),
emailCapsuleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
emailCapsuleView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
emailCapsuleView.heightAnchor.constraint(equalToConstant: Dimensions.inputFieldHeight)
])
Outside of a UINavigationController everything is fine,
but in a UINavigationController everythhing is super wide and broken (simulator shot)
The width is not the problem here - if I use a constant for the width, my subview is still off center despite having it's centerX sets to the view controller's view's center X.
Configure your navigation controller and after that try like this:
set your objects:
let emailTextfield: UITextField = {
let tf = UITextField()
tf.backgroundColor = .white
tf.layer.borderWidth = 1
tf.layer.borderColor = UIColor.lightGray.cgColor
tf.setPadding(left: 10, right: 10)// use my extension below
tf.attributedPlaceholder = NSAttributedString(string: "Email address", attributes: [.foregroundColor: UIColor.lightGray])
tf.layer.cornerRadius = 14
tf.clipsToBounds = true
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let passTextfield: UITextField = {
let tf = UITextField()
tf.backgroundColor = .white
tf.layer.borderWidth = 1
tf.layer.borderColor = UIColor.lightGray.cgColor
tf.setPadding(left: 10, right: 10)// use my extension below
tf.attributedPlaceholder = NSAttributedString(string: "Password", attributes: [.foregroundColor: UIColor.lightGray])
tf.layer.cornerRadius = 14
tf.clipsToBounds = true
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let loginButton: UIButton = {
let b = UIButton(type: .system)
b.backgroundColor = .black
b.setTitle("Save Image", for: .normal)
b.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
b.setTitleColor(.white, for: .normal)
b.layer.cornerRadius = 14
b.clipsToBounds = true
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
let subBigTitle: UILabel = {
let l = UILabel()
l.text = "Your Big Title"
l.font = .systemFont(ofSize: 30, weight: .regular)
l.textColor = .black
l.textAlignment = .center
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
Now in viewDidLoad set stackView, bigTitle label and constraints:
view.addSubview(subBigTitle)
subBigTitle.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
subBigTitle.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
subBigTitle.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
subBigTitle.heightAnchor.constraint(equalToConstant: 60).isActive = true
let stackView = UIStackView(arrangedSubviews: [emailTextfield, passTextfield, loginButton])
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 170).isActive = true
stackView.topAnchor.constraint(equalTo: subBigTitle.bottomAnchor, constant: 24).isActive = true
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
to add text padding to your textfield use my extension:
extension UITextField {
func setPadding(left: CGFloat, right: CGFloat){
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: left, height: self.frame.size.height))
self.leftView = paddingView
self.leftViewMode = .always
let paddingViewRight = UIView(frame: CGRect(x: 0, y: 0, width: right, height: self.frame.size.height))
self.rightView = paddingViewRight
self.rightViewMode = .always
}
}
and this is the result:

ViewController Layout Help Needed

I'm using XCode to build a UI for a countdown timer/metronome app. After struggling with layout constraints, I discovered vertical and horizontal stack views, which I thought would make things much easier.
However, the outermost stackview's boundaries appear to be outside the view of the device. I removed all the layout constraints, but that didn't make any difference.
Here's a partial screenshot of the project in XCode:
StackView boundaries
Sorry if this question has been asked before - I looked but haven't found anything.
I'm a professional software developer for the last 30 years or so. The languages I work with on a daily basis for the last 20 years or so are Java, C++, and Fortran. So dealing with UI layout in IOS is frustrating, to say the least!
Thanks in advance for any help!
Edit: Here's a screenshot of the uppermost stack view's size inspector: size inspector
I know your frustration well because it was mine too when I start, and I decided to give you a hand. Make all UI programmatically like this:
in your Controller class built your objects programmatically:
let buttonPlay: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.yourColor
let image = UIImage(systemName: "play.fill")
button.setImage(image, for: .normal)
button.tintColor = .black
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.layer.cornerRadius = 25
button.clipsToBounds = true
return button
}()
let buttonPause: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.yourColor
let image = UIImage(systemName: "pause.fill")
button.setImage(image, for: .normal)
button.tintColor = .black
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.layer.cornerRadius = 25
button.clipsToBounds = true
return button
}()
let buttonStop: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.yourColor
let image = UIImage(systemName: "stop.fill")
button.setImage(image, for: .normal)
button.tintColor = .black
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.layer.cornerRadius = 25
button.clipsToBounds = true
return button
}()
let minutesLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .white
label.text = "MM"
label.textColor = .gray
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
return label
}()
let secondsLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .white
label.text = "SS"
label.textColor = .gray
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
return label
}()
let separatorLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = ":"
label.textColor = .black
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
return label
}()
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.yourColor
label.text = "Title label"
label.textColor = .black
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false // this distactive automatic constraints
return label
}()
let timeLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "Tempo:"
label.textColor = .black
label.font = .systemFont(ofSize: 20, weight: .regular)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let imageViewTime: UIImageView = {
let image = UIImage(named: "yourImage")?.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image: image)
imageView.tintColor = .white
imageView.backgroundColor = .purple
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
let slider: UISlider = {
let s = UISlider()
s.maximumValue = 0
s.maximumValue = 100
s.isContinuous = true
s.tintColor = .black
s.addTarget(self, action: #selector(sliderInAction), for: .valueChanged) // call the function below to show slider value
s.translatesAutoresizingMaskIntoConstraints = false
return s
}()
let sliderLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "2"
label.textAlignment = .center
label.textColor = .black
label.font = .systemFont(ofSize: 16, weight: .regular)
label.widthAnchor.constraint(equalToConstant: 30).isActive = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let containerSlider: UIView = {
let v = UIView()
v.backgroundColor = UIColor.yourColor
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
In viewDidLoad set your background color and call the function to setup the programmatically constraints:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.yourColor
setupConstraints()
}
now set the constraints with autolayout:
fileprivate func setupConstraints() {
let stackViewTime = UIStackView(arrangedSubviews: [minutesLabel, separatorLabel, secondsLabel])
stackViewTime.distribution = .fillProportionally
stackViewTime.spacing = 4
stackViewTime.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackViewTime)
stackViewTime.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
stackViewTime.heightAnchor.constraint(equalToConstant: 50).isActive = true
stackViewTime.widthAnchor.constraint(equalToConstant: 148).isActive = true
stackViewTime.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
let stackViewPlayer = UIStackView(arrangedSubviews: [buttonPlay, buttonPause, buttonStop])
stackViewPlayer.distribution = .fillEqually
stackViewPlayer.spacing = 10
stackViewPlayer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackViewPlayer)
stackViewPlayer.widthAnchor.constraint(equalToConstant: 170).isActive = true
stackViewPlayer.heightAnchor.constraint(equalToConstant: 50).isActive = true
stackViewPlayer.centerXAnchor.constraint(equalTo: stackViewTime.centerXAnchor).isActive = true
stackViewPlayer.topAnchor.constraint(equalTo: stackViewTime.bottomAnchor, constant: 20).isActive = true
view.addSubview(imageViewTime)
imageViewTime.topAnchor.constraint(equalTo: stackViewPlayer.bottomAnchor, constant: 20).isActive = true
imageViewTime.heightAnchor.constraint(equalToConstant: 80).isActive = true
imageViewTime.widthAnchor.constraint(equalToConstant: 60).isActive = true
imageViewTime.centerXAnchor.constraint(equalTo: stackViewTime.centerXAnchor).isActive = true
view.addSubview(titleLabel)
titleLabel.topAnchor.constraint(equalTo: imageViewTime.bottomAnchor, constant: 20).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.addSubview(timeLabel)
timeLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10).isActive = true
timeLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
timeLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
timeLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
let stackSlider = UIStackView(arrangedSubviews: [sliderLabel, slider])
stackSlider.distribution = .fill
stackSlider.spacing = 10
stackSlider.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerSlider)
containerSlider.topAnchor.constraint(equalTo: timeLabel.bottomAnchor, constant: 0).isActive = true
containerSlider.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
containerSlider.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
containerSlider.heightAnchor.constraint(equalToConstant: 30).isActive = true
containerSlider.addSubview(stackSlider)
stackSlider.topAnchor.constraint(equalTo: containerSlider.topAnchor).isActive = true
stackSlider.bottomAnchor.constraint(equalTo: containerSlider.bottomAnchor).isActive = true
stackSlider.leadingAnchor.constraint(equalTo: containerSlider.leadingAnchor, constant: 10).isActive = true
stackSlider.trailingAnchor.constraint(equalTo: containerSlider.trailingAnchor, constant: -10).isActive = true
}
the last step set the function to show the slider value in your slider label when you move it:
#objc fileprivate func sliderInAction() {
sliderLabel.text = "\(Int(slider.value))"
}
This is the result:
Play with this code, an experienced developer like you should take a short time to write the code.

UIView Expand to Content inside

I have a function that returns a UIView. I want to dynamically change the size of my UIView(fullView) based on how much text is shown in the UITextView (thirdTextView). I currently have my height set at 200. How can I achieve a 'dynamic height'?
EDIT: Updated code so far. Is only working when a subView is added to the View. I don't want this...
func returnView(text1: String, text2: String) -> UIView {
let fullView = UIView()
fullView.backgroundColor = UIColor(red:0.82, green:0.83, blue:0.85, alpha:1.0)
fullView.translatesAutoresizingMaskIntoConstraints = false
let firstButton = UIButton()
firstButton.translatesAutoresizingMaskIntoConstraints = false
let secondButton = UIButton()
secondButton.translatesAutoresizingMaskIntoConstraints = false
let thirdTextView = UITextView()
thirdTextView.translatesAutoresizingMaskIntoConstraints = false
thirdTextView.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."
thirdTextView.backgroundColor = .clear
thirdTextView.isScrollEnabled = false
firstButton.setTitle("Button1", for: .normal)
firstButton.setTitleColor(.black, for: .normal)
firstButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
firstButton.contentHorizontalAlignment = .left
secondButton.setTitle("Button2", for: .normal)
secondButton.setTitleColor(.black, for: .normal)
secondButton.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)
secondButton.contentHorizontalAlignment = .right
let descriptionBarStackView = UIStackView(arrangedSubviews: [firstButton, UIView() ,secondButton])
descriptionBarStackView.translatesAutoresizingMaskIntoConstraints = false
descriptionBarStackView.axis = .horizontal
descriptionBarStackView.alignment = .fill
let viewWithStackViews = UIStackView(arrangedSubviews: [descriptionBarStackView, thirdTextView])
viewWithStackViews.translatesAutoresizingMaskIntoConstraints = false
viewWithStackViews.axis = .vertical
viewWithStackViews.layoutMargins = UIEdgeInsets(top: 15, left: 10, bottom: 10, right:10)
viewWithStackViews.isLayoutMarginsRelativeArrangement = true
fullView.addSubview(viewWithStackViews)
// view.addSubview(fullView)
fullView.leadingAnchor.constraint(equalTo: viewWithStackViews.leadingAnchor).isActive = true
fullView.trailingAnchor.constraint(equalTo: viewWithStackViews.trailingAnchor).isActive = true
fullView.topAnchor.constraint(equalTo: viewWithStackViews.topAnchor).isActive = true
fullView.bottomAnchor.constraint(equalTo: viewWithStackViews.bottomAnchor).isActive = true
fullView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -10).isActive = true
fullView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
fullView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
fullView.layer.cornerRadius = 5
return fullView
}
Give delegate of textview to the class
on textViewDidChange delegate method of textview, give height
In your scenario,
func textViewDidChange(_ textView: UITextView) {
thirdTextView.contentSize.height = textView.contentSize.height
}
Just set the width anchor, and it will dynamically expand to content size for height.
thirdTextView.widthAnchor.constraint(equalToConstant: 128.0).isActive = true

Self-sizing UILabel with UIView Subclassing

I have a sketch and I'm trying to achieve the concept programmatically.
Here's my Sketch:
Achievement:
My code:
let luna = UIView()
let descStrWidth = messageDescription.stringWidth // Using String Extension i made.
let descStrHeight = messageDescription.stringHeight // Using String Extension i made.
let titleStrHeight = messageTitle.stringHeight // ..
let titleStrWidth = messageTitle.stringWidth // ..
let titleLabel = UILabel()
titleLabel.text = messageTitle
titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
titleLabel.textColor = UIColor(hexString: "4A4A4A")
titleLabel.numberOfLines = 0 // whole idea of self-sizing
let titleDesc = UILabel()
titleDesc.text = messageDescription
titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
titleDesc.textColor = UIColor(hexString: "4A4A4A")
titleDesc.numberOfLines = 0
// My mainView
luna.frame = CGRect(x: 16, y: 40, width: screenWidth - 30, height: titleStrHeight + descStrHeight)
luna.center.x = luna.center.x
luna.backgroundColor = .white
luna.addShadow(radius: 11, opacity: 0.2) // Some Shadow
luna.layer.cornerRadius = 10
titleDesc.frame = CGRect(x: luna.frame.minX + 3, y: titleLabel.frame.maxY + titleStrHeight, width: luna.frame.width, height: descStrHeight)
titleLabel.frame = CGRect(x: luna.frame.minX + 3, y: 8, width: titleStrWidth, height: titleStrHeight)
luna.addSubview(titleLabel)
luna.addSubview(titleDesc)
self.view.addSubview(luna)
Getting provided string width and height:
extension String {
var stringWidth: CGFloat {
let constraintRect = CGSize(width: UIScreen.main.bounds.width, height: .greatestFiniteMagnitude)
let boundingBox = self.trimmingCharacters(in: .whitespacesAndNewlines).boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)], context: nil)
return boundingBox.width
}
var stringHeight: CGFloat {
let constraintRect = CGSize(width: UIScreen.main.bounds.width, height: .greatestFiniteMagnitude)
let boundingBox = self.trimmingCharacters(in: .whitespacesAndNewlines).boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)], context: nil)
return boundingBox.height
}
}
This is what I achieved so far. This might be easier with constraints, right?
Here's a solution by using auto layout and stack views.
let luna = UIView()
let titleLabel = UILabel()
titleLabel.text = "Title"
titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
//titleLabel.textColor = UIColor(hexString: "4A4A4A")
titleLabel.numberOfLines = 0 // whole idea of self-sizing
let titleDesc = UILabel()
titleDesc.text = "Description"
titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
//titleDesc.textColor = UIColor(hexString: "4A4A4A")
titleDesc.numberOfLines = 0
// My mainView
luna.backgroundColor = .white
//luna.addShadow(radius: 11, opacity: 0.2) // Some Shadow
luna.layer.cornerRadius = 10
let verticalStackView = UIStackView(arrangedSubviews: [titleLabel, titleDesc])
verticalStackView.axis = .vertical
let okButton = UIButton()
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.setContentHuggingPriority(.defaultHigh, for: .horizontal) // to stretch the okay button horizontally
let horizontalStackView = UIStackView(arrangedSubviews: [verticalStackView, okButton])
horizontalStackView.axis = .horizontal
luna.addSubview(horizontalStackView)
view.addSubview(luna)
horizontalStackView.translatesAutoresizingMaskIntoConstraints = false // when using autolayout from code, this property must be false, otherwise constraint won't work
luna.translatesAutoresizingMaskIntoConstraints = false
// This method activates all constraint (when you create a constraint with anchors, by default they are disabled)
NSLayoutConstraint.activate([
horizontalStackView.topAnchor.constraint(equalTo: luna.topAnchor, constant: 8),
horizontalStackView.bottomAnchor.constraint(equalTo: luna.bottomAnchor, constant: -8),
horizontalStackView.leadingAnchor.constraint(equalTo: luna.leadingAnchor, constant: 8),
horizontalStackView.trailingAnchor.constraint(equalTo: luna.trailingAnchor, constant: -8),
luna.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
luna.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30),
luna.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30)
])
You can change distances between views by changing spacing property of stack view. (i.e. horizontalStackView.spacing = 16 to put a space of 16 points between the two label and the okay button)
You can try this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let luna = UIView()
let titleLabel = UILabel()
titleLabel.text = "messageTitle"
titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
titleLabel.textColor = UIColor.blue
titleLabel.numberOfLines = 0 // whole idea of self-sizing
let titleDesc = UILabel()
titleDesc.text = "messageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionvmessageDescriptionmessageDescriptionmessageDescription"
titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
titleDesc.textColor = UIColor.red
titleDesc.numberOfLines = 0
let btn = UIButton()
btn.setTitle("Okay", for: .normal)
btn.setTitleColor(UIColor.blue, for: .normal)
luna.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleDesc.translatesAutoresizingMaskIntoConstraints = false
btn.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(luna)
luna.layer.cornerRadius = 10
luna.addSubview(titleLabel)
luna.addSubview(titleDesc)
luna.addSubview(btn)
luna.layer.borderColor = UIColor.green.cgColor
luna.layer.borderWidth = 2
btn.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000), for: .horizontal)
NSLayoutConstraint.activate([
luna.topAnchor.constraint(equalTo: view.topAnchor, constant: 30),
luna.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
luna.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
titleLabel.topAnchor.constraint(equalTo: luna.topAnchor, constant: 20),
titleLabel.leadingAnchor.constraint(equalTo: luna.leadingAnchor, constant: 20),
titleLabel.trailingAnchor.constraint(equalTo: btn.leadingAnchor, constant: -10),
titleDesc.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
titleDesc.leadingAnchor.constraint(equalTo: luna.leadingAnchor, constant: 20),
titleDesc.trailingAnchor.constraint(equalTo: btn.leadingAnchor, constant: -10),
titleDesc.bottomAnchor.constraint(equalTo: luna.bottomAnchor, constant: -20),
btn.centerYAnchor.constraint(equalTo: luna.centerYAnchor, constant: 0 ),
btn.trailingAnchor.constraint(equalTo: luna.trailingAnchor, constant: -20),
])
}
As people are saying I would suggest you look into auto layout for the future but for now this should do what you want:
let luna = UIView()
let titleLabel = UILabel()
titleLabel.text = messageTitle
titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
titleLabel.textColor = UIColor(hexString: "4A4A4A")
titleLabel.numberOfLines = 0 // whole idea of self-sizing
let titleDesc = UILabel()
titleDesc.text = messageDescription
titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
titleDesc.textColor = UIColor(hexString: "4A4A4A")
titleDesc.numberOfLines = 0
// My mainView
// The margins of the labels inside the luna view.
let margins = UIEdgeInsetsMake(8, 4, 8, 4)
// The vertical space between the two labels.
let labelSpace: CGFloat = 4
let titleDescSize = titleDesc.sizeThatFits(CGSize(width: screenWidth - 30 - margins.left - margins.right, height: .greatestFiniteMagnitude))
let titleLabelSize = titleLabel.sizeThatFits(CGSize(width: screenWidth - 30 - margins.left - margins.right, height: .greatestFiniteMagnitude))
luna.frame = CGRect(x: 16, y: 40, width: screenWidth - 30, height: titleLabelSize.height + titleDescSize.height + margins.top + margins.bottom + labelSpace)
luna.center.x = luna.center.x
luna.backgroundColor = .white
luna.layer.borderWidth = luna.addShadow(radius: 11, opacity: 0.2) // Some Shadow
luna.layer.cornerRadius = 10
titleLabel.frame = CGRect(x: margins.left, y: margins.top, width: titleLabelSize.width, height: titleLabelSize.height)
titleDesc.frame = CGRect(x: margins.left, y: titleLabel.frame.origin.y + titleLabel.frame.size.height + labelSpace, width: titleDescSize.width, height: titleDescSize.height)
luna.addSubview(titleLabel)
luna.addSubview(titleDesc)
self.view.addSubview(luna)
Note: You don't even need to use you string extension as the UILabels are capable of calculating the size they need based on their current settings.
You are using avenirnext-demibold and avenirnext-regular custom font. But when you are calculating string width and Height you're using system font UIFont.systemFont(ofSize: 14). You're not getting the correct width and height of the string. titleLabel and titleDesc both labels width should be less then luna width for Padding.

Resources