IOS swift expandable view [closed]
Asked Answered
T

3

6

I would like to expand an view when I switch a switch to on, and to collapse when I switch the switch to off. At the same time all the other elements I have in my design have to move down or up.

Here is how the layout looks like when the switches are OFF

And here is how the layout looks like when the switches are ON:

I hope you understand what I am trying to do.

And the question... How do I do that???

Tympan answered 29/7, 2014 at 11:31 Comment(3)
Yes I understand what you are trying to do. So what is the question?Tillotson
The question... How do i do that?Tympan
One way is to use a UITableView and insert/delete rows dynamically (or just reloadData) when the switches are changed. One example: #9856472Smollett
M
7

It's much easier to do with AutoLayout and inside InterfaceBuilder (with a xib or storyboard), so foremost familiarize yourself with them if needed.

  1. Wrap a container UIView around the UISwitches and make sure to set it to Clip Subviews.
  2. Add a constraint for the container view's height and set it to 0.
  3. Drag that constraint into your class declaration to generate an IBOutlet property.
  4. Drag a new outlet from your UISwitch 'Value Changed' event to the class to generate an IBAction method.
  5. Make sure to implement that method so that when the value changes, based on the change you toggle-animate the height constraint (the IBOutlet)

    self.firstHeightConstraint.constant = on ? 200.0 : 0.0
    self.view.setNeedsUpdateConstraints()
    UIView.animateWithDuration( ... 
        options:.BeginFromCurrentState, 
        animations: { self.view.layoutIfNeeded() } ...
    )
    
Marishamariska answered 7/8, 2014 at 7:24 Comment(0)
C
1

Using UIView, it's not a good way. You should use UITableView instead of views.

Using TableView, you can add UISwitch with title according to the first image, in UITableView's header and return required number of rows in changed section in TableView on state ON and return the number of rows 0 on state OFF.

Still an issue, please let me know I'll help you.

Colombi answered 28/11, 2018 at 5:21 Comment(0)
G
0

enter image description here

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)
    }
}

Gravante answered 16/3, 2021 at 5:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.