class LabeledSwitchView: UIView { let theLabel: UILabel = { let v = UILabel() v.translatesAutoresizingMaskIntoConstraints = false v.backgroundColor = .cyan v.numberOfLines = 0 return v }() let theSwitch: UISwitch = { let v = UISwitch() v.translatesAutoresizingMaskIntoConstraints = false return v }() override init(frame: CGRect) { super.init(frame: frame) commonInit() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonInit() } func commonInit() -> Void { addSubview(theLabel) addSubview(theSwitch) NSLayoutConstraint.activate([ // at least 8-pts top and bottom spacing for the label theLabel.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8.0), theLabel.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0), // at least 8-pts top and bottom spacing for the switch theSwitch.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8.0), theSwitch.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0), // 8-pts leading padding for the label theLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0), // 8-pts trailing padding for the switch theSwitch.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0), // center the switch vertically with the label theSwitch.centerYAnchor.constraint(equalTo: theLabel.centerYAnchor, constant: 0.0), // 8-pts horizontal between the label and the switch theLabel.trailingAnchor.constraint(equalTo: theSwitch.leadingAnchor, constant: -8.0), ]) } } class HupkesViewController: UIViewController { let theStack: UIStackView = { let v = UIStackView() v.translatesAutoresizingMaskIntoConstraints = false v.axis = .vertical v.alignment = .fill v.distribution = .fill return v }() let theScrollView: UIScrollView = { let v = UIScrollView() v.translatesAutoresizingMaskIntoConstraints = false v.backgroundColor = .orange return v }() let theData = [ "WordOne", "WordOne WordTwo", "WordOne WordTwo WordThree", "WordOne WordTwo WordThree WordFour", "WordOne WordTwo WordThree WordFour WordFive", "WordOne WordTwo WordThree WordFour WordFive WordSix", ] override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor(red: 0.25, green: 0.5, blue: 1.0, alpha: 1.0) view.addSubview(theScrollView) theScrollView.addSubview(theStack) NSLayoutConstraint.activate([ // constrain the scroll view to top / leading / trailing theScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0.0), theScrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0.0), theScrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0.0), // short scroll view, so we can get some scrolling theScrollView.heightAnchor.constraint(equalToConstant: 240.0), // constrain the stack view 20-pts top / bottom / leading / trailing theStack.topAnchor.constraint(equalTo: theScrollView.topAnchor, constant: 20.0), theStack.bottomAnchor.constraint(equalTo: theScrollView.bottomAnchor, constant: -20.0), theStack.leadingAnchor.constraint(equalTo: theScrollView.leadingAnchor, constant: 20.0), theStack.trailingAnchor.constraint(equalTo: theScrollView.trailingAnchor, constant: -20.0), // constrain stack view width equal to scroll view width - 40 (20-pts on each side) theStack.widthAnchor.constraint(equalTo: theScrollView.widthAnchor, multiplier: 1.0, constant: -40.0), ]) // create and add the custom views to the stack view theData.forEach { s in let v = LabeledSwitchView() v.translatesAutoresizingMaskIntoConstraints = false v.backgroundColor = .white v.theLabel.text = s v.theSwitch.isOn = true theStack.addArrangedSubview(v) // if it's not the last one, add a "separator line" if s != theData.last { let v = UIView() v.translatesAutoresizingMaskIntoConstraints = false v.backgroundColor = .lightGray v.heightAnchor.constraint(equalToConstant: 1.0).isActive = true theStack.addArrangedSubview(v) } } } }