So I'm trying to make a UIScrollView
that is basically a menu navigation bar that a user can navigate by swiping between menu items, where there are pages vertically laid out, and sub-pages horizontally laid out. Mockup:
The way I'm doing this is by creating a UIScrollView
whose frame is the size of one UILabel
, and I have isPagingEnabled
set to true. I then tried adding a UIStackView
, with each row indicating a page, and the contents of each row being a sub-page. I set the scrollView.contentSize
to be the size of the UIStackView
. The problem is that all my label's frames are zeros all through and the UIScrollView
doesn't work.
I really wanted to avoid getting help as I felt like I could do this by myself but I've spent two days on this and I've lost all hope.
Here is the code where I add the labels. It's called upon the scrollview's superview's ini
t (because the UIScrollView
is in a custom UIView
I call crossNavigation View).
private func addScrollViewLabels() {
//Get Max Number of Items in a Single Row
var maxRowCount = -1
for item in items {
if (item.contents.count > maxRowCount) {maxRowCount = item.contents.count}
}
self.rowsStackView.axis = .vertical
self.rowsStackView.distribution = .fillEqually
self.rowsStackView.alignment = .fill
self.rowsStackView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.addSubview(rowsStackView)
for i in 0 ..< items.count {
let row = items[i].contents
let rowView = UIView()
self.rowsStackView.addArrangedSubview(rowView)
var rowLabels : [UILabel] = []
//First Label
rowLabels.append(UILabel())
rowView.addSubview(rowLabels[0])
NSLayoutConstraint(item: rowLabels[0], attribute: .leading, relatedBy: .equal, toItem: rowView, attribute: .leading, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: rowLabels[0], attribute: .top, relatedBy: .equal, toItem: rowView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: rowLabels[0], attribute: .bottom, relatedBy: .equal, toItem: rowView, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: rowLabels[0], attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 0.55, constant: 0.0).isActive = true
//Middle Labels
for j in 1 ..< row.count {
rowLabels.append(UILabel())
rowView.addSubview(rowLabels[j])
//Stick it to it's left
NSLayoutConstraint(item: rowLabels[j], attribute: .leading, relatedBy: .equal, toItem: rowLabels[j-1], attribute: .trailing, multiplier: 1.0, constant: 0.0).isActive = true
//Stick top to rowView
NSLayoutConstraint(item: rowLabels[j], attribute: .top, relatedBy: .equal, toItem: rowView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
//Row Height is equal to rowView's Height
NSLayoutConstraint(item: rowLabels[j], attribute: .height, relatedBy: .equal, toItem: rowView, attribute: .height, multiplier: 1.0, constant: 0.0).isActive = true
//rowLabels[j].width = containerView.width * 0.55 (so other labels can peek around it)
NSLayoutConstraint(item: rowLabels[j], attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 0.55, constant: 0.0).isActive = true
}
//lastLabel.trailing = rowView.trailing
NSLayoutConstraint(item: rowLabels[row.count-1], attribute: .trailing, relatedBy: .equal, toItem: rowView, attribute: .trailing, multiplier: 1.0, constant: 0.0).isActive = true
}
//Constraints for stack view:
//rowsStackView.height = scrollView.height * items.count
NSLayoutConstraint(item: rowsStackView, attribute: .height, relatedBy: .equal, toItem: scrollView, attribute: .height, multiplier: CGFloat(self.items.count), constant: 0.0).isActive = true
//rowsStackView.height = scrollView.height * items.count
NSLayoutConstraint(item: rowsStackView, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .leading, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: rowsStackView, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: rowsStackView, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 1.0, constant: 0.0).isActive = true
self.scrollView.contentSize = rowsStackView.frame.size
}
Take a look at this demo project I made based off your mock up. It sets up the framework for everything you want to do.
Examine the view hierarchy and you'll understand where to configure the layout to make it exactly how you want.
The last step is to tweak the paging so you get the snap behavior you desire. There are many ways to do this but it is a bit involved.
Good Luck!