I know my code isnt the cleanest, but it bothers me when answers tell the questioner its not a good way or do it this way instead. I am offering a solution NOT using table views or constraints:
In your example, you can use tags for each "level" that needs to be moved. So starting with switch 2 and all views below that, you would have a tag for that level (the second hidden collapsable view would have a tag of 2 as well), then switch 3 would have a tag of 3.
For the labels hidden, you are adding them to a subview i call insideView1 or insideView2. The frames of those inside views should have the proper minX, minY and width, but have a height of 0 to start. Also importantly they have layer.masksToBounds = false, so their subviews dont show when their height is 0. We then animate the height changing to the proper height (expanding), as well as all subviews under changing position.
Lastly i made a dictionary to keep track of when a section is expanded so you can use the same function to expand/collapse other areas while one is already expanded.
import UIKit
class ExpandViewController: UIViewController {
var insideView1 = UIView()
var insideView2 = UIView()
var expand = [1:true,2:true]
@objc func expandCollapse(_ sender : UIButton) {
var heightChange = CGFloat(0)
switch sender.tag {
case 1:
heightChange = 90
default:
heightChange = 90
}
var viewsToExpand = [UIView(),insideView1,insideView2]
UIView.animate(withDuration: 1.0) { [self] in
let expandMe = viewsToExpand[sender.tag]
if expand[sender.tag] == true {
expandMe.frame.size.height = heightChange
for v in view.subviews {
if v.tag > sender.tag {
v.center.y += heightChange
}
}
} else {
expandMe.frame.size.height = 0
for v in view.subviews {
if v.tag > sender.tag {
v.center.y -= heightChange
}
}
}
expand[sender.tag] = !expand[sender.tag]!
}
}
override func viewDidLoad() {
super.viewDidLoad()
let switcher1 = UISwitch()
switcher1.tag = 1
switcher1.isOn = false
switcher1.frame = CGRect(x: view.bounds.width - 150, y: 100, width: 40, height: 30)
switcher1.addTarget(self, action: #selector(expandCollapse(_:)), for: .allTouchEvents)
view.addSubview(switcher1)
let switchLabel1 = UILabel()
switchLabel1.tag = 1
switchLabel1.text = "Switch 1"
switchLabel1.frame = CGRect(x: 30, y: 0, width: view.bounds.width, height: 30)
switchLabel1.center.y = switcher1.center.y
view.addSubview(switchLabel1)
let switcher2 = UISwitch()
switcher2.tag = 2
switcher2.isOn = false
switcher2.frame = CGRect(x: view.bounds.width - 150, y: 140, width: 40, height: 30)
switcher2.addTarget(self, action: #selector(expandCollapse(_:)), for: .allTouchEvents)
view.addSubview(switcher2)
let switchLabel2 = UILabel()
switchLabel2.tag = 2
switchLabel2.text = "Switch 2"
switchLabel2.frame = CGRect(x: 30, y: 0, width: view.bounds.width, height: 30)
switchLabel2.center.y = switcher2.center.y
view.addSubview(switchLabel2)
let switcher3 = UISwitch()
switcher3.tag = 3
switcher3.isOn = false
switcher3.frame = CGRect(x: view.bounds.width - 150, y: 180, width: 40, height: 30)
// switcher3.addTarget(self, action: #selector(expandCollapse(_:)), for: .allTouchEvents)
view.addSubview(switcher3)
let switchLabel3 = UILabel()
switchLabel3.tag = 3
switchLabel3.text = "Switch 3"
switchLabel3.frame = CGRect(x: 30, y: 0, width: view.bounds.width, height: 30)
switchLabel3.center.y = switcher3.center.y
view.addSubview(switchLabel3)
insideView1.frame = CGRect(x: 0, y: switcher1.frame.maxY + 5, width: view.bounds.width, height: 0)
insideView1.layer.backgroundColor = UIColor.lightGray.cgColor
insideView1.layer.masksToBounds = true
insideView1.tag = 1
view.addSubview(insideView1)
insideView2.frame = CGRect(x: 0, y: switcher2.frame.maxY + 5, width: view.bounds.width, height: 0)
insideView2.layer.backgroundColor = UIColor.lightGray.cgColor
insideView2.layer.masksToBounds = true
insideView2.tag = 2
view.addSubview(insideView2)
let insideLabel1 = UILabel()
insideLabel1.text = "Label"
insideLabel1.frame = CGRect(x: 60, y: 10, width: view.bounds.width, height: 30)
insideView1.addSubview(insideLabel1)
let insideTextField1 = UITextField()
insideTextField1.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField1.center.y = insideLabel1.center.y
insideTextField1.layer.backgroundColor = UIColor.white.cgColor
insideView1.addSubview(insideTextField1)
let insideLabel2 = UILabel()
insideLabel2.text = "Label"
insideLabel2.frame = CGRect(x: 60, y: 50, width: view.bounds.width, height: 30)
insideView1.addSubview(insideLabel2)
let insideTextField2 = UITextField()
insideTextField2.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField2.center.y = insideLabel2.center.y
insideTextField2.layer.backgroundColor = UIColor.white.cgColor
insideView1.addSubview(insideTextField2)
let insideLabel3 = UILabel()
insideLabel3.text = "Label"
insideLabel3.frame = CGRect(x: 60, y: 10, width: view.bounds.width, height: 30)
insideView2.addSubview(insideLabel3)
let insideTextField3 = UITextField()
insideTextField3.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField3.center.y = insideLabel3.center.y
insideTextField3.layer.backgroundColor = UIColor.white.cgColor
insideView2.addSubview(insideTextField3)
let insideLabel4 = UILabel()
insideLabel4.text = "Label"
insideLabel4.frame = CGRect(x: 60, y: 50, width: view.bounds.width, height: 30)
insideView2.addSubview(insideLabel4)
let insideTextField4 = UITextField()
insideTextField4.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField4.center.y = insideLabel4.center.y
insideTextField4.layer.backgroundColor = UIColor.white.cgColor
insideView2.addSubview(insideTextField4)
}
}