Nested Stack Views: Child Stack View is not inside its parent stack view when attaching it programmatically - swift

I'm trying to implement nested stack views in which one stack view is inside another stack view. I've based my code here. My current code is here below.
#IBOutlet weak var verticalStackView: UIStackView!
let blueImageView = UIImageView()
blueImageView.backgroundColor = UIColor.blueColor()
blueImageView.image = UIImage(named: "just some image")
blueImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let greenImageView = UIImageView()
greenImageView.backgroundColor = UIColor.greenColor()
greenImageView.image = UIImage(named: "just some image")
// This is just from SnapKit
greenImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Horizontal
stackView.distribution = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 16.0
stackView.addArrangedSubview(blueImageView)
stackView.addArrangedSubview(greenImageView)
stackView.translatesAutoresizingMaskIntoConstraints = false;
// This is just from SnapKit
verticalStackView.snp_makeConstraints { (make) in
make.height.equalTo(70)
}
verticalStackView.addSubview(stackView)
When I tried running it looks this way.
As you can see, the sub stack view stackView is below the parent stack view (verticalStackView) in the hierarchy. But the positioning is off.
I'm quite new with Swift, AutoLayout and StackViews. Anyone that can help point out what I'm missing here?
Thanks!

So I've figured out the solution after some reading..
#IBOutlet weak var verticalStackView: UIStackView!
let blueImageView = UIImageView()
blueImageView.backgroundColor = UIColor.blueColor()
blueImageView.image = UIImage(named: "buttonFollowCheckGreen")
blueImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let greenImageView = UIImageView()
greenImageView.backgroundColor = UIColor.greenColor()
greenImageView.image = UIImage(named: "buttonFollowCheckGreen")
greenImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let firstLineStackView = UIStackView()
firstLineStackView.axis = UILayoutConstraintAxis.Horizontal
firstLineStackView.distribution = UIStackViewDistribution.Fill
firstLineStackView.alignment = UIStackViewAlignment.Center
firstLineStackView.spacing = 8.0
firstLineStackView.addArrangedSubview(blueImageView)
firstLineStackView.addArrangedSubview(greenImageView)
firstLineStackView.translatesAutoresizingMaskIntoConstraints = false;
let redImageView = UIImageView()
redImageView.backgroundColor = UIColor.redColor()
redImageView.image = UIImage(named: "buttonFollowCheckGreen")
redImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
verticalStackView.addArrangedSubview(firstLineStackView)
I just had to use addArrangedSubview instead of addSubview and let Auto layout do the rest. Just to fix the positioning and size of the image views, I've also modified the alignment of the verticalStackView from fill to leading.

greenImageView.heightAnchor.constraint(equalToConstant: 34).isActive = true
greenImageView.widthAnchor.constraint(equalToConstant: 34).isActive = true
and it will be solved

Related

stack programmatically, giving space for bottom

i am trying to place the image below the text i add
class SolicitudViewController: BaseViewController {
lazy var imagePrincipal : UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "diseño")
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
//imageView.heightAnchor.constraint(equalToConstant: 50).isActive = true
//imageView.bottomAnchor.constraint(equalToConstant: 100).isActive = true
return imageView
}()
lazy var stackView : UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.distribution = .fill
stack.translatesAutoresizingMaskIntoConstraints = false
stack.addArrangedSubview(imagePrincipal)
//stack.addArrangedSubview(lblsubTitulo)
//stack.addArrangedSubview(lineView)
stack.addArrangedSubview(imageEvaluando2)
stack.addArrangedSubview(imageEvaluando3)
return stack
}()
lazy var scrollView : UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
setSubtitle(subtitle: "test View")
}
I have tried to use this: stack.setCustomSpacing(30, after: imagePrincipal) but it positions the image on top, I want the image to be below the text

Displaying property with Horizontal and Vertical Stack View in Swift

I am new to swift . I am following the programmatic approach to create the view . I created two stack view . One is horizontal and other one is vertical. Into horizontal stack view I want to display the label property and Vertical stack view I want to display the Image. I want to display the image on left side and label properties on right side.
Here is the code I used ..
import UIKit
class PeopleCell: UITableViewCell {
static let identifier = "PeopleCell"
private lazy var mainStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .leading
return stackView
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fill
stackView.alignment = .center
return stackView
}()
private lazy var lastnameTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var firstnameTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var peopleImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
// imageView.backgroundColor = .blue
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(firstName: String, lastName: String) {
firstnameTitleLabel.text = "Firstname :\(firstName)"
lastnameTitleLabel.text = "Lastname : \(lastName)"
}
func configureImageCell(row: Int, viewModel: ViewModel) {
peopleImageView.image = nil
viewModel
.downloadImage(row: row) { [weak self] data in
let image = UIImage(data: data)
self?.peopleImageView.image = image
}
}
private func setUpUI() {
stackView.addArrangedSubview(lastnameTitleLabel)
stackView.addArrangedSubview(firstnameTitleLabel)
mainStackView.addArrangedSubview(peopleImageView)
mainStackView.addArrangedSubview(stackView)
contentView.addSubview(mainStackView)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
mainStackView.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 10).isActive = true
mainStackView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -10).isActive = true
peopleImageView.heightAnchor.constraint(equalToConstant: 140).isActive = true
peopleImageView.widthAnchor.constraint(equalToConstant: 140).isActive = true
stackView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor).isActive = true
}
}
Here is the screenshot ..
Here is the expected result.
The main problem is that you have your .axis properties reversed.
You want your mainStackView.axis to be .horizontal and your stackView.axis to be .vertical.
Also, these two lines are not needed (and cause problems):
// don't do this
//stackView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor).isActive = true
//stackView.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor).isActive = true
As a side note, instead of:
let safeArea = contentView.safeAreaLayoutGuide
you may want to use:
let safeArea = contentView.layoutMarginsGuide
which gives you the default cell margins.
Edit
// this is the "main" stack view with
// the image on the left
// the labels on the right
// so it needs to be HORIZONTAL
private lazy var mainStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
//stackView.axis = .vertical
stackView.axis = .horizontal
stackView.distribution = .fill
return stackView
}()
// this is the "labels" stack view with
// two (or more) labels from top down
// so it needs to be VERTICAL
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
//stackView.axis = .horizontal
stackView.axis = .vertical
stackView.distribution = .fill
return stackView
}()

Navigation bar display with UIImage and UILabel for title

I want to put navigation bar title with UIImage and UILabel.
let image = UIImage(systemName: "flame")
imageView.tintColor = R.color.fire()
self.navigationItem.titleView = imageView
self.navigationItem.title = "표시한 제품"
self.navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: R.color.myPageMenu]
This is my code, but this code only shows UIImage(systemName: "flame").
let image = UIImage(systemName: "flame")
let title = "표시한 제품"
let imageView = UIImageView()
imageView.image = image
let textLabel = UILabel()
textLabel.text = text
textLabel.textAlignment = .center
//Stack View
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.horizontal
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 4.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
self.navigationItem.titleView = stackView
Here is how you can add UIImageView and UILabel in the Navigation bar.
File: ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var navView: UIView!
#IBOutlet weak var navTitle: UILabel!
#IBOutlet weak var navImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
setNavTitleView()
}
func setNavTitleView() {
navTitle.text = "표시한 제품"
navTitle.textColor = .lightGray
navImage.image = UIImage(systemName: "flame")
navImage.tintColor = .red
navigationController?.navigationBar.backgroundColor = .black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.lightGray]
navigationController?.navigationBar.tintColor = .lightGray
navigationItem.titleView = navView
}
}
Storyboard:
Output:

Can't figure out why my stackviews are not arranging vertically

I have done this many times but this time for some reason won't work the way it usually does. Am I doing something wrong here? I am just trying to get two views into my UIstackView and distribute them vertically. It seems to keep overlapping and going all over the place. At one point it was only even showing one view.
My viewdidload():
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
view.addSubview(headerView)
view.addSubview(contentView)
contentView.addSubview(contentStack)
headerView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.30).isActive = true
headerView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
headerView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
headerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
contentView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.70).isActive = true
contentView.widthAnchor.constraint(equalTo: self.view.widthAnchor,multiplier: 0.90).isActive = true
contentView.topAnchor.constraint(equalTo: headerView.bottomAnchor,constant: 20).isActive = true
contentView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
My Views and Labels:
fileprivate lazy var headerView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
return view
}()
fileprivate lazy var contentView : UIView = {
var view = UIView()
view.backgroundColor = .lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
fileprivate lazy var contentStack : UIStackView = {
var stack = UIStackView(arrangedSubviews: [EarningsView,ListingsView,])
stack.translatesAutoresizingMaskIntoConstraints = true
stack.distribution = .fillEqually
stack.alignment = .fill
stack.axis = .vertical
// stack.spacing = 5
return stack
}()
fileprivate lazy var EarningsView : UIView = {
let EarningsView = UIView()
EarningsView.translatesAutoresizingMaskIntoConstraints = false
EarningsView.backgroundColor = .blue
EarningsView.addSubview(EarningsLabel)
EarningsView.addViewBorder(borderColor: UIColor.black.cgColor, borderWith: 0.5, borderCornerRadius: 0.0)
return EarningsView
}()
fileprivate lazy var EarningsLabel : UILabel = {
let EarningsLabel = UILabel()
EarningsLabel.translatesAutoresizingMaskIntoConstraints = false
let earningsText = NSAttributedString(string: "My Earnings", attributes: self.stringAttrib)
EarningsLabel.attributedText = earningsText
EarningsLabel.textColor = .black
EarningsLabel.backgroundColor = .white
EarningsLabel.textAlignment = .center
return EarningsLabel
}()
fileprivate lazy var ListingsView : UIView = {
let ListingsView = UIView()
ListingsView.translatesAutoresizingMaskIntoConstraints = false
ListingsView.addSubview(ListingLabel)
ListingsView.addViewBorder(borderColor: UIColor.black.cgColor, borderWith: 0.5, borderCornerRadius: 0.0)
ListingsView.backgroundColor = .red
return ListingsView
}()
fileprivate lazy var ListingLabel : UILabel = {
let ListingLabel = UILabel()
ListingLabel.translatesAutoresizingMaskIntoConstraints = false
let listingText = NSAttributedString(string: "My Listing", attributes: self.stringAttrib)
ListingLabel.attributedText = listingText
return ListingLabel
}()
This is driving me crazy because I have done it so many times before and now I have spent atleast 6 hours on this little part trying to figure it out. Of course I could just create a whole new viewcontroller but I just want to figure this out.
EarningsView, EarningsLabel, ListingsView, ListingLabel have
translatesAutoresizingMaskIntoConstraints = false
But they have no constraints added to replace the Autoresizing constraints, so they just act goofy and go up to the top left. I've seen stuff like this before. On my stuff. Either autosize or put in constraints.

StackView: Overriding custom UIview intrinsicContent size produces very unexpected outcomes

After struggling with programmatic dynamic UI elements for the last few weeks I decided I would give UIstackView a try.
I want custom UIView classes to occupy the stackview with different heights based upon user Input I would remove, add views on the fly.
I found out that stackViews base their 'cell' height upon the UI element's intrinsic content size. However, UIViews do not have one. I searched far and wide and found out that I need to override the View's intrisicContentsize function with one where I can explicitly set the width and height.
However results are very unpredictable and I'm sure there is some little thing that I just do not know dat causes this weird behaviour. Since I'm new to the language and there are a LOT of gotcha's I'm just gonna paste the code here and hope you'll be able to spot what I'm doing wrong.
I've read the docs ofc, a lot of articles, they all point to that override funcion that does not seem to work form me.
This is my mainViewController class;
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
var bv = BackButtonView(frame: CGRect.zero, image: UIImage(named: "backArrow.png")!)
var redView : UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}();
var blueView : UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .blue
return view
}();
let stack : UIStackView = {
let stack = UIStackView();
stack.translatesAutoresizingMaskIntoConstraints = false;
stack.axis = .vertical
stack.distribution = .fillProportionally;
stack.spacing = 8
return stack;
}();
override func viewDidLoad() {
let view = UIView(frame: UIScreen.main.bounds)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
self.view = view
self.view.addSubview(stack)
createLayout()
bv.intrinsicContentSize
stack.addArrangedSubview(bv)
print(bv.frame)
print(bv.intrinsicContentSize)
stack.layoutIfNeeded()
stack.addArrangedSubview(redView)
stack.addArrangedSubview(blueView)
}
private func setConstraints(view: UIView) -> [NSLayoutConstraint] {
return [
view.heightAnchor.constraint(equalToConstant: 50),
]
}
private func createLayout() {
stack.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
stack.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stack.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = MyViewController()
Now here's my custom UIClass that is giving me all this trouble:
import UIKit
public class BackButtonView : UIView {
public var size = CGSize(width: 10, height: UIView.noIntrinsicMetric)
override open var intrinsicContentSize: CGSize {
return size
}
var button : UIButton;
public init(frame: CGRect, image: UIImage) {
button = UIButton()
super.init(frame : frame);
self.translatesAutoresizingMaskIntoConstraints = false;
self.backgroundColor = .black
self.addSubview(button)
setupButton(image: image);
print(button.intrinsicContentSize)
}
let backButtonTrailingPadding : CGFloat = -18
lazy var buttonConstraints = [
button.heightAnchor.constraint(equalToConstant: 50),
button.widthAnchor.constraint(equalToConstant: 50),
button.centerYAnchor.constraint(equalTo: self.centerYAnchor),
button.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: backButtonTrailingPadding),
]
private func setupButton(image: UIImage) {
self.button.translatesAutoresizingMaskIntoConstraints = false;
self.button.setImage(image, for: .normal);
self.button.contentMode = .scaleToFill
NSLayoutConstraint.activate(buttonConstraints)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
struct anchors {
var topAnchor = NSLayoutConstraint()
var bottomAnchor = NSLayoutConstraint()
var leadingAnchor = NSLayoutConstraint()
var trailingAnchor = NSLayoutConstraint()
}
}
You can see that the specified width in the size proprrty gets ignored.
Under the above conditions., this is the output
If I now change the my custom UIClass's intrisiContentSize height to anyting else, this happens:
public var size = CGSize(width: UIView.noIntrinsicMetric, height: 10)
override open var intrinsicContentSize: CGSize {
return size
}
result:
Please help me figure out what is and isn't going on
The final screen should look something like this: