We have already discussed through examples and implemented UIStackView in a tutorial before. In this tutorial, we’ll learn how to add a UIStackView inside a UIScrollView and also implement adding and removing views from a UIStackView programmatically and animate them.
UIStackView inside UIScrollView
Following are the steps you need to follow in order to add a UIStackView inside a UIScrollView.
- Add the UIScrollView to the View Controller View and set the constraints to all the sides of the View.
- Add a UIStackView inside the UIScrollView. Set the constraints: Leading, Trailing, Top & Bottom to the ScrollView.
- If the StackView is to be scrolled Vertically, set an Equal Width Constraint between the UIStackView and the UIScrollView.
- If the StackView is to be scrolled Horizontally, set an Equal Height Constraint between the UIStackView and the UIScrollView.
- Add your SubViews inside the UIStackView and enjoy scrolling.
Let’s demonstrate this in our XCode Project Main.storyboard
.
Now let’s add a few Buttons inside the StackView:
The output in the simulator is given below:
Let’s look at building a UIStackView programmatically next.
UIStackView programmatically
Open up your ViewController.swift file and add the following lines of code:
import UIKit
class ViewController: UIViewController {
var stackView = UIStackView()
var constraintBottom : NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let textField = UITextField()
textField.placeholder = "Email"
textField.textColor = UIColor.darkGray
textField.minimumFontSize = 17.0
textField.borderStyle = UITextField.BorderStyle.roundedRect
textField.keyboardType = UIKeyboardType.emailAddress
textField.returnKeyType = UIReturnKeyType.next
let textFieldPassword = UITextField()
textFieldPassword.placeholder = "Password"
textFieldPassword.textColor = UIColor.darkGray
textFieldPassword.minimumFontSize = 17.0
textFieldPassword.textAlignment = .right
textFieldPassword.isSecureTextEntry = true
textFieldPassword.borderStyle = UITextField.BorderStyle.roundedRect
textFieldPassword.keyboardType = UIKeyboardType.default
textFieldPassword.returnKeyType = UIReturnKeyType.done
let button = UIButton(type: UIButton.ButtonType.system)
button.setTitle("Login", for: UIControl.State.normal)
button.setTitleColor(UIColor.white, for: UIControl.State.normal)
button.backgroundColor = UIColor.black
button.layer.borderColor = UIColor.white.cgColor
button.layer.borderWidth = 1.0
button.layer.cornerRadius = 5.0
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.tag = 1
stackView = UIStackView(arrangedSubviews: [textField, textFieldPassword, button])
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 20.0
view.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
constraintBottom = stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40)
constraintBottom?.isActive = true
}
@objc func buttonAction(sender: UIButton!) {
if(sender.tag == 1){
constraintBottom?.isActive = false
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100).isActive = true
}
}
}
In this code, we’ve set UITextField and Buttons inside a UIStackView vertically which are filled equally. We set the constraints on the stackView using NSLayoutConstraint.
The subViews are passed in the arrangeSubView array. On Button click, we set a selector to disable the bottom constraint of the StackView and add a new one which increases the bottom margin.
translatesAutoresizingMaskIntoConstraints
must be set to false since UIStackViews use Auto-layouts.
isHidden property is used to toggle the visibility of a complete StackView including all it’s elements.
Loading SubViews using Function
Instead of passing individual subViews in the array, we can populate the UIStackView using a function as shown below:
class ViewController: UIViewController {
var stackView = UIStackView()
var constraintBottom : NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
stackView = UIStackView(arrangedSubviews: createButtonArray(named: "1","2","3","4"))
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 20.0
view.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
constraintBottom = stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40)
constraintBottom?.isActive = true
}
func createButtonArray(named: String...) -> [UIButton]
{
return named.map{name in
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Button (name)", for: .normal)
button.backgroundColor = UIColor.red
button.setTitleColor(UIColor.white, for: .normal)
return button
}
}
}
createButtonArray
creates an array by transforming the strings into their corresponding Button instances to compose the array.
Adding SubViews On Click
Let’s configure each of the above Buttons to add another subView on button click. We will be adding a nested subview.
The viewDidLoad method remains the same as before. The other methods in the ViewController.swift are:
func createButtonArray(named: String...) -> [UIButton]
{
return named.map{name in
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Button (name)", for: .normal)
button.backgroundColor = UIColor.red
button.setTitleColor(UIColor.white, for: .normal)
button.tag = named.firstIndex(of: name)!
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}
}
@objc func buttonAction(sender: UIButton!) {
print("Button tag is")
print(sender.tag)
createNestedSubView(sender.tag)
}
func createNestedSubView(_ tag: Int)
{
let ss = UIStackView(arrangedSubviews: createButtonArray(named: "(tag+1).1","(tag+1).2","(tag+1).3","(tag+1).4"))
ss.axis = .horizontal
ss.translatesAutoresizingMaskIntoConstraints = false
ss.distribution = .fillEqually
ss.alignment = .fill
ss.spacing = 5.0
stackView.insertArrangedSubview(ss, at: tag)
ss.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
ss.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
ss.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
}
Thus, on button click, we pass the index of the button. A nested horizontal StackView is created.
The new stack view is inserted at the tag number index.
insertArrangedSubview
is used to insert the SubView in the UIStackView at a certain index.
arrangeSubView
is used to insert the new SubView at the end of the UIStackView.
Removing SubViews Programmatically
We can remove the SubViews using the function removeArrangedSubview on the UIStackView instance.
Also, we need to remove the view from the SuperView too.
In the ViewController.swift createButtonArray function we add another button click action for the nested StackView buttons. When any of them are clicked they would be deleted:
func createButtonArray(named: String...) -> [UIButton]
{
return named.map{name in
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Button (name)", for: .normal)
button.backgroundColor = UIColor.red
button.setTitleColor(UIColor.white, for: .normal)
button.tag = named.firstIndex(of: name)!
if(name.contains("."))
{
button.addTarget(self, action: #selector(deleteAction), for: .touchUpInside)
}
else{
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
}
return button
}
}
@objc func deleteAction(sender: UIButton!) {
print("Delete tag is")
print(sender.titleLabel?.text! ?? "NA")
stackView.removeArrangedSubview(sender)
sender.removeFromSuperview()
}
The output of the application in action is given below:
Animating SubViews
We can animate the subViews when the isHidden property is toggled using:
UIView.animate(withDuration: 0.5){
button.isHidden = true //or false
}
In the above application, we hide and animate the Button 4 when it is clicked:
@objc func buttonAction(sender: UIButton!) {
print("Button tag is")
print(sender.tag)
if(sender.titleLabel?.text?.contains("4"))!
{
UIView.animate(withDuration: 0.5){
sender.isHidden = true //or false
}
}
createNestedSubView(sender.tag)
}
This brings an end to this tutorial. You can download the project from the link below: