iOS - UIView does not display inside UIStackView - ios

I have a UIStackView which needs a background color in one of the stacks, so I placed a UIView inside, but the UIView is never displayed. The UIView currently has one subview, a UILabel, but should eventually have another UIStackView instead. If I display the UILabel as a direct child of the UIStackView, rather than the UIView, then the UILabel displays properly. So, what constraints are missing or wrong on my UIView?
let stack = UIStackView()
stack.axis = .Vertical
stack.alignment = .Leading
stack.distribution = .EqualCentering
stack.spacing = 0
let width = UIScreen.mainScreen().bounds.size.width
let frame = CGRect(x: 0, y: 0, width: width, height: 50)
let viewHolder = UIView(frame: frame)
viewHolder.backgroundColor = blueColor
//Needs a blue background
let name = UILabel()
name.text = nameText
name.textColor = yellowColor
name.backgroundColor = blueColor
name.font = UIFont.boldSystemFontOfSize(32.0)
let address = UILabel()
address.text = addressText
let dateTime = UILabel()
dateTime.text = calString
viewHolder.addSubview(name)
stack.addArrangedSubview(viewHolder)
stack.addArrangedSubview(address)
stack.addArrangedSubview(dateTime)
self.stackView.addArrangedSubview(stack)
And when run it produces this:
If I remove the UIView from the equation, ie:
let stack = UIStackView()
stack.axis = .Vertical
stack.alignment = .Leading
stack.distribution = .EqualCentering
stack.spacing = 0
let width = UIScreen.mainScreen().bounds.size.width
let frame = CGRect(x: 0, y: 0, width: width, height: 50)
let viewHolder = UIView(frame: frame)
viewHolder.backgroundColor = blueColor
//Needs a blue background
let name = UILabel()
name.text = nameText
name.textColor = yellowColor
name.backgroundColor = blueColor
name.font = UIFont.boldSystemFontOfSize(32.0)
let address = UILabel()
address.text = addressText
let dateTime = UILabel()
dateTime.text = calString
//viewHolder.addSubview(name)
stack.addArrangedSubview(name) //Add the label directly to the UIStackView
stack.addArrangedSubview(address)
stack.addArrangedSubview(dateTime)
self.stackView.addArrangedSubview(stack)
I get:
I just need the blue part to stretch across the screen

So, answer is, don't use UIStackView. Use the built in UITableView for this kind of layout.

Related

I cannot stretch the height of the titleView in the UINavigationBar

This is my code:
class UINavigationControllerCustom : UINavigationController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews();
navigationBar.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 120);
}
}
class PreferenceInput: UIViewController {
override func viewDidLoad() {
let l1 = UILabel()
l1.backgroundColor = .clear
l1.numberOfLines = 1
l1.font = UIFont.boldSystemFont(ofSize: 30.0)
l1.textAlignment = .left
l1.textColor = .black
l1.text = "Bold title"
l1.sizeToFit();
let l2 = UILabel()
l2.backgroundColor = .clear
l2.numberOfLines = 2
l2.font = UIFont.boldSystemFont(ofSize: 16.0)
l2.textAlignment = .left
l2.textColor = .darkGray;
l2.text = "This is a\nmultiline string for the navBar"
l2.sizeToFit();
let tView = UIStackView(arrangedSubviews: [l1,l2]);
tView.axis = .vertical;
let spacer = UIView()
let constraint = spacer.widthAnchor.constraint(greaterThanOrEqualToConstant: CGFloat.greatestFiniteMagnitude)
constraint.isActive = true
constraint.priority = .defaultLow
let stack = UIStackView(arrangedSubviews: [tView, spacer])
stack.axis = .horizontal
navigationItem.titleView = stack
navigationItem.titleView.sizeToFit();
}
}
And this is what I get:
I can set the height of the navigation bar but how and where should I set the constraint to stretch the height in respect of the given 120px of the custom navigation controller?
I want to provide an individual height of the navigation bar.

Adding UITextView and UIImageView to UIStackView

So, I added some text (UITextView) to my stackView and centered to the top. I also added a UIImageView which would sit nicely under my UITextView. Well it doesn't. For some reason the image covers the text completely. If I delete the image the text comes back up nice on the top center. Played a lot with the stack distribution and alignment but no luck. Not sure what I'm missing :(. Any help is appreciated!
I'm adding both the UITextView and UIIMageView as arrangedSubview to the stack.
Here is my code:
//stack
let stack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 5
stack.distribution = .fillProportionally
stack.alignment = .fill
return stack
}()
//text
fileprivate let title: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.contentMode = .scaleAspectFit
title.layer.cornerRadius = 10
title.backgroundColor = .darkGray
title.font = UIFont(name: "Megrim-Regular", size: 17)
title.textColor = .white
title.textAlignment = .center
return title
}()
//image
let image: UIImageView = {
let image = UIImageView()
image.image = UIImage(named: "demoPic.jpg")
image.translatesAutoresizingMaskIntoConstraints = false
image.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
return image
}()
Hope this below may help,
I think your issue is relating to constraints applied to the stackview and the holder view. (See below)
Your UI Elements (TextView & Image) code seems to be fine (maybe the image will not be work with 50 width /50 height inside this particular stack view configuration. It will require a different approach IMO.
Nevertheless on my playground in order to see it, I just applied 2 constraints towards my container view in order to see your TextView well above your ImageView as you wanted.
Here is the playground I used to reproduce your issue, you can copy and paste it to see if it fits what you request.
import UIKit
import PlaygroundSupport
/// DEMO VIEW CLASS
final class DemoView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented")
}
}
// YOUR UI CODE
//stack
let stack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 5
stack.distribution = .fillProportionally
stack.alignment = .fill
return stack
}()
//text
fileprivate let title: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.contentMode = .scaleAspectFit
title.layer.cornerRadius = 10
title.backgroundColor = .darkGray
title.font = UIFont(name: "Megrim-Regular", size: 17)
title.text = "TextView"
title.textColor = .white
title.textAlignment = .center
return title
}()
//image
let image: UIImageView = {
let image = UIImageView()
image.backgroundColor = .red
image.translatesAutoresizingMaskIntoConstraints = false
image.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
return image
}()
// PLAYGROUND DEMO VIEW TO HOLD YOUR STACK VIEW
let demoView = DemoView(frame: CGRect(x: 0, y: 0, width: 350, height: 150))
stack.addArrangedSubview(title)
stack.addArrangedSubview(image)
demoView.addSubview(stack)
demoView.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[stackView]-0-|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil,
views: ["stackView": stack])
)
demoView.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[stackView]-0-|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil,
views: ["stackView": stack])
)
PlaygroundPage.current.liveView = demoView
Results: Your Text View is above the image center (ImageView just have a RED Background here).

IOS| UILabel not visible in UIStack view

I am trying to build a vertical stack view with a UI label and multiple horizontal stack views, However, I only see the multiple horizontal stacks in the UI and not the UI label. Here is some code
final class FinalView: UIView {
let mainStackView = UIStackView()
init() {
super.init(frame: .zero)
let label: UILabel = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Sorry ?"
...
let verticalStackView = UIStackView(arrangedSubviews: multiplehorizontalViews)
verticalStackView.axis = .vertical
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.spacing = 5.0
mainStackView.addArrangedSubview(label)
mainStackView.addArrangedSubview(verticalStackView)
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.axis = .vertical
}
}
I am only seeing verticalStackView but not label. When I removed this mainStackView.addArrangedSubview(verticalStackView) from the code, I can see the label. But When it is there I can't see the label. Any ideas on the issue or any tips for debugging?
Your code is working completely fine. Just checked on iOS 13 with below code
let mainStackView = UIStackView()
mainStackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mainStackView)
var vflString = "V:|-[mainStackView]-|"
var vflConstraints = NSLayoutConstraint.constraints(withVisualFormat: vflString, options: [], metrics: nil, views: ["mainStackView" : mainStackView])
view.addConstraints(vflConstraints)
vflString = "H:|-[mainStackView]-|"
vflConstraints = NSLayoutConstraint.constraints(withVisualFormat: vflString, options: [], metrics: nil, views: ["mainStackView" : mainStackView])
view.addConstraints(vflConstraints)
let label: UILabel = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Sorry ?"
let label1 = UILabel(frame: .zero)
label1.text = "Label 1"
let label2 = UILabel(frame: .zero)
label2.text = "Label 2"
let label3 = UILabel(frame: .zero)
label3.text = "Label 3"
let multiplehorizontalViews = [label1, label2, label3]
let verticalStackView = UIStackView(arrangedSubviews: multiplehorizontalViews)
verticalStackView.axis = .horizontal
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.spacing = 5.0
verticalStackView.distribution = .fillProportionally
mainStackView.addArrangedSubview(label)
mainStackView.addArrangedSubview(verticalStackView)
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.axis = .vertical
I'm pretty sure it's not your case but in mine
this applied:
UIBezierPath: roundedRect: byRoundingCorners: cornerRadii: acts weird
bezier path on layer was cutting all content from the stackview
you can check in the visual debugger what's going in: if the labels
text show up there then it's an issue with layer clipping

UIStackView adjust height to subviews

I want the UIStackView to resize to fit their subviews (UIImageView and UILabel in this case)
let headerView = UIStackView()
headerView.axis = .vertical
headerView.alignment = .center
headerView.distribution = .equalSpacing
headerView.spacing = 10
let headerImage = UIImageView(...)
headerImage.contentMode = .scaleAspectFill
headerImage.clipsToBounds = true
headerImage.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: tableView.frame.width / 1.618)
let desciptionView = UILabel()
desciptionView.text = "Some very long text wrapping multiple lines..."
desciptionView.numberOfLines = 0
desciptionView.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
headerView.addArrangedSubview(headerImage)
headerView.addArrangedSubview(desciptionView)
print(headerView.bounds) // always 0,0,0,0
print(headerView.frame) // always 0,0,0,0
tableView.tableHeaderView = headerView
(in this code height and width are 0)
How to implement the wanted behaviour?
The only way I have found to do this is by setting the content hugging and compression resistance priorities of the child views to be required, something like this:
let arrangedViews = [filterLabel, image]
.map { (view: UIView) -> UIView in
view.setContentHuggingPriority(.required, for: .horizontal)
view.setContentCompressionResistancePriority(.required, for: .horizontal)
return view }
let stack = UIStackView(arrangedSubviews: arrangedViews)
stack.axis = .horizontal
stack.distribution = .fill
stack.spacing = 8
With those set on the arranged subviews, do you do indeed (or at least I did) get the desired result.

How to make iOS navigationItem.titleView align to the left?

navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = nil
head = UIView()
head.frame = CGRectMake(0, 0, 200, 44)
head.frame.origin.x = CGFloat(0)
navigationItem.titleView = head
I attempt to align the titleView to the left, but it still remains in the middle.
Try this:
let title = UILabel()
title.text = "TITLE"
let spacer = UIView()
let constraint = spacer.widthAnchor.constraint(greaterThanOrEqualToConstant: CGFloat.greatestFiniteMagnitude)
constraint.isActive = true
constraint.priority = .defaultLow
let stack = UIStackView(arrangedSubviews: [title, spacer])
stack.axis = .horizontal
navigationItem.titleView = stack
The idea is that main view (in this case title) will take all the space it needs and spacer view will take all the free space left.
I figured it out.
I just need to set my custom UIView as the leftBarButtonItem.
I had a similar requirement of adding the Title along with the subtitle to the left of the navbar. I couldn't achieve it with a TitleView since it cannot be aligned left.
So I took #TIMEZ and #Wain's answers, along with responses from the thread here and added a complete answer, in case it helps anyone :
let titleLabel = UILabel()
titleLabel.text = "Pillars"
titleLabel.textAlignment = .center
titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
let subtitleLabel = UILabel()
subtitleLabel.text = "How did you do today?"
subtitleLabel.textAlignment = .center
subtitleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.subheadline)
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.distribution = .equalSpacing
stackView.alignment = .leading
stackView.axis = .vertical
let customTitles = UIBarButtonItem.init(customView: stackView)
self.navigationItem.leftBarButtonItems = [customTitles]
You can't align a title view to the left. You can create a title view and add a subview positioned to its left. If you're looking to display in place of the back button then you should be using a bar button item instead of title view.
I don't think Apple wants you to do that. Navigation bars have a pretty specific purpose that often involves having something else in the top left corner like a Back button. You might be better off making a custom UIView or UIToolbar that looks like the navigation bar.
If it's a custom UIView, override intrinsicContentSize and return
CGSize(width: .greatestFiniteMagnitude, height: UIView.noIntrinsicMetric)
This will stretch the view to the entire width between left and right bar button items.
You can constraint the titleView to the navigationBars leftAnchor.
private func setupNavigationBarTitleView() {
let titleView = YourCustomTitleView()
navigationBarTitleView = titleView
navigationBarTitleView?.translatesAutoresizingMaskIntoConstraints = false
navigationItem.titleView = navigationBarTitleView
if let navigationBar = navigationController?.navigationBar {
NSLayoutConstraint.activate([
titleView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor, constant: 16),
titleView.heightAnchor.constraint(equalToConstant: 36)
])
}
}
The simplest solution is to add low priority constraint for title width.
let titleLabel = UILabel()
titleLabel.textAlignment = .left
...
let c = titleLabe.widthAnchor.constraint(equalToConstant: 10000)
c.priority = .required - 1
c.isActive = true
navigationItem.titleView = titleLabel
Hey guys after trying most of the solutions above. I found that most of em still did not meet my prod requirements. Here is a solution I came up with after trying out different solutions.
func setLeftAlignTitleView(font: UIFont, text: String, textColor: UIColor) {
guard let navFrame = navigationController?.navigationBar.frame else{
return
}
let parentView = UIView(frame: CGRect(x: 0, y: 0, width: navFrame.width*3, height: navFrame.height))
self.navigationItem.titleView = parentView
let label = UILabel(frame: .init(x: parentView.frame.minX, y: parentView.frame.minY, width: parentView.frame.width, height: parentView.frame.height))
label.backgroundColor = .clear
label.numberOfLines = 2
label.font = font
label.textAlignment = .left
label.textColor = textColor
label.text = text
parentView.addSubview(label)
}
let navLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.width - 32, height: view.frame.height))
navLabel.text = "Hi, \(CurrentUser.firstName)"
navLabel.textColor = UIColor.white
navLabel.font = UIFont.systemFont(ofSize: 20)
navigationItem.titleView = navLabel

Resources