UIStackView not displaying - swift

In my viewController viewDidLoad method I have:
let stackView = UIStackView()
stackView.axis = .Vertical
self.view.addSubview(stackView)
for _ in 1..<100{
let vw = UIButton(type: .System)
vw.setTitle("Button", forState: .Normal)
stackView.addArrangedSubview(vw)
}
but when I compile I only get a totally white screen.
What am I doing wrong?

You're adding the stack view to the parent view, but you're not telling the parent view how to lay the stack view out. If you're using constraint based layout, you need to pin the stack view to some of the edges of the parent view:
view.addConstraint(NSLayoutConstraint(item: stackView, Attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1.0, constant: 0.0)
view.addConstraint(NSLayoutConstraint(item: stackView, Attribute: .Leading, relatedBy: .Equal, toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 0.0)
view.addConstraint(NSLayoutConstraint(item: stackView, Attribute: .Trailing, relatedBy: .Equal, toItem: self.view, attribute: .Trailing, multiplier: 1.0, constant: 0.0)
This will pin the stack view to the top, leading and trailing edges of the view.
The other approach would be to use Interface Builder to set up the constraints, as they are pretty verbose to manage in code.

You have add constraints or set frame for your stack view. Please check modified code below :
let stackView = UIStackView()
stackView.axis = .Vertical
stackView.distribution = .FillEqually
for i in 1..<100{
let vw = UIButton(type: .System)
vw.setTitle("Button\(i)", forState: .Normal)
stackView.addArrangedSubview(vw)
}
self.view.addSubview(stackView)
stackView.frame = self.view.bounds

Related

items inside UIStackView overflowing

I've created a UIStackView programmatically and inside placed UITextViews.
when the number of the UITextViews is more than 5 the boxes start overflowing off the screen. Here's a screenshot of the situation:
And here's the code :
let stackview = UIStackView(arrangedSubviews: letterBoxes)
stackview.axis = .horizontal
stackview.spacing = 10
stackview.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackview)
stackview.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackview.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
what am I missing?
thank you
Also add .leading and .trailing constraints like this
view.addConstraint(NSLayoutConstraint(item: stackview, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: stackview, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0))
so the complete code looks like:
let stackview = UIStackView(arrangedSubviews: letterBoxes)
stackview.axis = .horizontal
stackview.spacing = 10
stackview.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackview)
stackview.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackview.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
view.addConstraint(NSLayoutConstraint(item: stackview, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: stackview, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0))

Adding Constraint messed up view

I have a view with a UICollectionView and a UISegmentedControl.
I want to change constraints so the segment controller won't overlap collection view, like in this picture:
This is my code :
override func viewDidLoad()
{
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = nil
self.tabBarController?.tabBar.isHidden = true
self.SegmentController.setTitle(SegmentAtext, forSegmentAt: 0)
self.SegmentController.setTitle(SegmentBtext, forSegmentAt: 1)
self.view.bringSubview(toFront: SegmentController)
self.LoadProducts(productsToShow: SegmentAtype)
}
SO I add this command:
self.ProductsCollection.topAnchor.constraint(equalTo: SegmentController.bottomAnchor, constant: 10).isActive = true
But the result is worse:
Now the segment controller is almost completely hidden!
How do I fix this?
Edit:
My viewDidLayoutSubviews function:
override func viewDidLayoutSubviews()
{
ProductsCollection.translatesAutoresizingMaskIntoConstraints = false
let topConstraint = NSLayoutConstraint(item: ProductsCollection, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 20)
let bottomConstraint = NSLayoutConstraint(item: ProductsCollection, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: -50) //leaving space for search field
let leadingConstraint = NSLayoutConstraint(item: ProductsCollection, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: ProductsCollection, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: 0)
self.view.addConstraints([topConstraint, bottomConstraint, leadingConstraint, trailingConstraint])
}
Notice:
my viewDidLayoutSubviews is implemented in the super view, which does not contain a UISegmentedControl. the UISegmentedControl is contained in an inheriting view.
Edit: An updated view
If you would want the UISegmentedControl to be centred on the screen and below it to have your collection view you would do
NSLayoutConstraint.activate([
segmentedControl.topAnchor.constraint(equalTo: view.topAnchor),
segmentedControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
collectionView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor),
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor),
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
So we have segmented controls top be top of the view, and center it,
then top of collection view is bottom of segmented control (you can add a constant for padding if needed) and left right and bottom to the view

Swift 3 - Create title bar constraints programmatically

I am using Swift 3, iOS 10, XCode 8.2.
In my code, I need to create a UIViewController programmatically and hence, specify its layout and content programmatically as well.
#IBAction func testViewController() {
let detailViewController = UIViewController()
detailViewController.view.backgroundColor = UIColor.white
let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.blue
label.text = "Scan Results"
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = UIColor.white
return label
}()
let titleConstraints: [NSLayoutConstraint] = [
NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: titleLabel, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0),
NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
]
detailViewController.view.addSubview(titleLabel)
detailViewController.view.addConstraints(titleConstraints)
self.navigationController?.pushViewController(detailViewController, animated: true)
}
In the vertical view (ignore all the other junk; just focus on the blue title bar):
But in the horizontal view:
What is the correct constraint to set so that it takes up the entire width of the bar and there isn't that extra space from the top since the status bar disappears when horizontal?
EDIT
After making #thexande suggestions, I do get an error:
[LayoutConstraints] The view hierarchy is not prepared for the
constraint: <NSLayoutConstraint:0x608000098100
UILabel:0x7fe35b60edc0'Scan Results'.left ==
UIView:0x7fe35b405c20.left (inactive)> When added to a view, the
constraint's items must be descendants of that view (or the view
itself). This will crash if the constraint needs to be resolved before
the view hierarchy is assembled. Break on
-[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug. 2017-02-24 21:01:59.807 EOB-Reader[78109:10751346] * Assertion failure in
-[UIView _layoutEngine_didAddLayoutConstraint:roundingAdjustment:mutuallyExclusiveConstraints:],
/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.6.21/NSLayoutConstraint_UIKitAdditions.m:649
2017-02-24 21:01:59.951 EOB-Reader[78109:10751346] * Terminating app
due to uncaught exception 'NSInternalInconsistencyException', reason:
'Impossible to set up layout with view hierarchy unprepared for
constraint.'
I've also updated my code in the original post.
The reason this is happening is because you are using frames. You calculated the frame based on the width of the screen. You do not need frames, you can do this all using auto layout. Instead, you should use constraints to pin your label to it's super view bounds, and give it a static height. for example:
lazy var titleConstraints: [NSLayoutConstraint] = [
NSLayoutConstraint(item: self.titleLabel, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.titleLabel, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.titleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
]
then, in viewDidLoad()
self.view.addConstraints(titleConstraints)
You could simplify your label declaration like so. dont forget the auto resizing mask flag to get constraints to work correctly:
let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.blue
label.text = "Scan Results"
label.textAlignment = .center
label.textColor = UIColor.white
return label
}()
Finally, you are doing strange math to get the top of your view controller to abut the bottom of your nav bar controller. Remove all that garbage and put the following in viewDidLoad() to get the top of your view controller right against the bottom of your UINavigationBar:
self.edgesForExtendedLayout = []
UPDATES:
The problem here is you are appending views and constraints into a View Controller which has not allocated yet.
The reason we append sub views and constraints within viewDidLoad() is because we cannot add subviews and constraints before the view....did....load into memory. Otherwise, it's not there, and you get the error above. Consider breaking out your detailViewController into a class declaration, like so:
class detailViewController: UIViewController {
let eobTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.blue
label.text = "Scan Results"
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = UIColor.white
return label
}()
lazy var eobTitleConstraints: [NSLayoutConstraint] = [
NSLayoutConstraint(item: self.eobTitleLabel, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.eobTitleLabel, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.eobTitleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.eobTitleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
]
override func viewDidLoad() {
self.view.addSubview(eobTitleLabel)
self.view.addConstraints(self.eobTitleConstraints)
}
}
Also, not to come off as offensive, but your code is kind of a mess. Things you should avoid in the future:
adding constraints to a label which does not exist. ( rename the label of fix the constraints)
you are declaring vars in a outlet method. dont do this, declare methods and properties at the class level.
Read about OOP and how it is implemented in swift. This will help you understand the methods and patterns to complete your task :)

UIPageControl has background on tvOS

In one of my games I am using a UICollectionView as the level select menu. I recently added a UIPageControl to it programatically.
/// Page control
func setupPageControl() {
pageControl.hidesForSinglePage = true
pageControl.numberOfPages = DataSource.worlds
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = DataSource.pageControlColors[pageControl.currentPage]
pageControl.pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.8)
pageControl.addTarget(self, action: #selector(didPressPageControl), for: .valueChanged)
view.addSubview(pageControl)
let leading = NSLayoutConstraint(item: pageControl, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let trailing = NSLayoutConstraint(item: pageControl, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
let bottomConstant = Device.isPad ? view.frame.width / 9 : view.frame.width / 17
let bottom = NSLayoutConstraint(item: pageControl, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: -bottomConstant)
view.addConstraints([leading, trailing, bottom])
}
Everything is fine on iOS but on tvOS the PageController has a translucent background that stretches all across the screen.
How can I turn this off? I tried setting the pageControl background color but that does not seem to work.
As usual just when I post a question I find the answer a minute later.
You can remove the background on tvOS by calling this
#if os(tvOS)
for subview in pageControl.subviews {
let effectView = subview as? UIVisualEffectView
effectView?.removeFromSuperview()
}
#endif
Change background color for page control

Adding NSLayoutConstraints to UISegmentedControl causes UISegmentedControl to disappear

I have a UISegmentedControl defined programmatically.
I am trying to add a layout constraint so that when my iPad rotates, the segmented control sizes correctly within the rotated view rather than spill off screen.
I apply the following constraint:
streamSegmentedControl.translatesAutoresizingMaskIntoConstraints = false
let segmentedControlWidth = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Width,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Width,
multiplier: 1.0,
constant: -10.0)
containerView.addConstraint(segmentedControlWidth)
My UIsegmentControl is defined as follows:
streamSegmentedControl = UISegmentedControl(items: ["Today's Events", "Past Events"])
streamSegmentedControl.frame = CGRectMake(-10,containerView.frame.size.height*0.3,containerView.frame.width+20,40)
streamSegmentedControl.selectedSegmentIndex = 0
streamScope = "today"
streamSegmentedControl.setTitleTextAttributes(segmentedControlFont as [NSObject : AnyObject], forState: .Normal)
streamSegmentedControl.backgroundColor = UIColor.colorFromClass("background")
streamSegmentedControl.tintColor = UIColor.colorFromClass("default")
streamSegmentedControl.addTarget(self, action: "changeStreamScope:", forControlEvents: UIControlEvents.ValueChanged)
containerView.addSubview(streamTableView)
containerView.addSubview(streamSegmentedControl)
I don't get an error, but at runtime, my segmented control disappears. Not sure what I am missing here as I've only done auto layout within storyboards in the past.
I only want to be able to adjust the width of the segmented control, so I assume I only need a single layout constraint.
Can anyone give me some direction? Thanks.
You need to add three constraints at least. Leading, top and trailing.
I am weak in swift but your constraints should look like.
let segmentedControlTop = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Top,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Top,
multiplier: 1.0,
constant: 0.0)
let segmentedControlLeading = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Leading,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Leading,
multiplier: 1.0,
constant: 0.0)
let segmentedControlTrailing = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Trailing,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Trailing,
multiplier: 1.0,
constant: 0.0)
containerView.addConstraint(segmentedControlTop)
containerView.addConstraint(segmentedControlLeading)
containerView.addConstraint(segmentedControlTrailing)
If you want to keep a 5 point offset from superview, try playing with constant values of leading and trailing using 5 points.