iOS view visibility gone

24,738

Solution 1

As I have worked on both iOS & Android, You need to play with constraint outlet in ios to achieve Android functioning. iOS Does not support automatically like Android native support on visibility GONE & VISIBLE

You need to hook the outlet of particular constraint(it may vertical/horizontal/height) you need to set it to 0 & need to manage your UI.

To Hide:

self.viewYourConstraint.constant = 0
self.yourView.hidden = true
self.view.layoutIfNeeded()

To Show:

self.viewYourConstraint.constant = 100//your constant value
self.yourView.hidden = false
self.view.layoutIfNeeded()

Note: If other constraints will be affected because of the update to the constraint above, the following must also need to be called:

self.yourView.setNeedsUpdateConstraints()

Cheers

Solution 2

Try this extension:

extension UIView {

    func visiblity(gone: Bool, dimension: CGFloat = 0.0, attribute: NSLayoutAttribute = .height) -> Void {
        if let constraint = (self.constraints.filter{$0.firstAttribute == attribute}.first) {
            constraint.constant = gone ? 0.0 : dimension
            self.layoutIfNeeded()
            self.isHidden = gone
        }
    }
}

// How you can use this....

@IBOutlet weak var testView: UIView?
@IBAction func testVisibilty(switchbutton: UISwitch) -> Void {

    let viewHeight:CGFloat = switchbutton.isOn ? 100 : 0.0
    self.testView?.visiblity(gone: !switchbutton.isOn, dimension: viewHeight)

    // set visibility for width constraint
    //let viewWidth:CGFloat = switchbutton.isOn ? 300 : 0.0
    //self.testView?.visiblity(gone: !switchbutton.isOn, dimension: viewWidth, attribute: .width)

}

Here is result:

enter image description here

Solution 3

Maybe you'd prefer this solution

extension UIView {

    enum Visibility {
        case visible
        case invisible
        case gone
    }

    var visibility: Visibility {
        get {
            let constraint = (self.constraints.filter{$0.firstAttribute == .height && $0.constant == 0}.first)
            if let constraint = constraint, constraint.isActive {
                return .gone
            } else {
                return self.isHidden ? .invisible : .visible
            }
        }
        set {
            if self.visibility != newValue {
                self.setVisibility(newValue)
            }
        }
    }

    private func setVisibility(_ visibility: Visibility) {
        let constraint = (self.constraints.filter{$0.firstAttribute == .height && $0.constant == 0}.first)

        switch visibility {
        case .visible:
            constraint?.isActive = false
            self.isHidden = false
            break
        case .invisible:
            constraint?.isActive = false
            self.isHidden = true
            break
        case .gone:
            if let constraint = constraint {
                constraint.isActive = true
            } else {
                let constraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0)
                self.addConstraint(constraint)
                constraint.isActive = true
            }
        }
    }
}

then the usage is:

someView.visibility = .visible
someView.visibility = .invisible
someView.visibility = .gone

edit:

improving capabilities: will work from storyboard (just write: 'visible', 'invisible', 'gone') in the "Visibility State"

all constraints inside view should be less then a 1000

extension UIView {

    enum Visibility: String {
        case visible = "visible"
        case invisible = "invisible"
        case gone = "gone"
    }

    var visibility: Visibility {
        get {
            let constraint = (self.constraints.filter{$0.firstAttribute == .height && $0.constant == 0}.first)
            if let constraint = constraint, constraint.isActive {
                return .gone
            } else {
                return self.isHidden ? .invisible : .visible
            }
        }
        set {
            if self.visibility != newValue {
                self.setVisibility(newValue)
            }
        }
    }

    @IBInspectable
    var visibilityState: String {
        get {
            return self.visibility.rawValue
        }
        set {
            let _visibility = Visibility(rawValue: newValue)!
            self.visibility = _visibility
        }
    }

    private func setVisibility(_ visibility: Visibility) {
        let constraints = self.constraints.filter({$0.firstAttribute == .height && $0.constant == 0 && $0.secondItem == nil && ($0.firstItem as? UIView) == self})
        let constraint = (constraints.first)

        switch visibility {
        case .visible:
            constraint?.isActive = false
            self.isHidden = false
            break
        case .invisible:
            constraint?.isActive = false
            self.isHidden = true
            break
        case .gone:
            self.isHidden = true
            if let constraint = constraint {
                constraint.isActive = true
            } else {
                let constraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0)
                // constraint.priority = UILayoutPriority(rawValue: 999)
                self.addConstraint(constraint)
                constraint.isActive = true
            }
            self.setNeedsLayout()
            self.setNeedsUpdateConstraints()
        }
    }
}

Solution 4

You say you want to hide your subview and remove the space it occupies. If you want to avoid messing with constraints, you can try using stack views:

  • Create a UIStackView and setup appropriate constraints between the stack view and its parent view;
  • Add views to the stack view;
  • Now you can toggle the visibility of these views inside the stack view by setting view.isHidden to true or false, and the layout will adjust automatically.

Solution 5

I believe you are looking for view.isHidden = true. This simply hides the view in place without altering the view hierarchy or constraint mapping. You can subsequently re-show your view . with view.isHidden = false.

Share:
24,738
John
Author by

John

Updated on February 18, 2020

Comments

  • John
    John about 4 years

    I am new to iOS development.I want to toggle (hide/visible) a subview from parent view.In android there is a way to hide the visibility to gone.

    In android

    subView.setVisibility(View.GONE);
    

    In iOS

    subView.removeFromSuperview()
    

    when i use above function it remove subViewConstraints and mess up my scroll view constraints.

    topsubView.bottomAnchor.constraint(equalTo: bottomSubView.topAnchor, constant: 8).isActive = true
    

    when i use the above code it works fine and hide subView.but when i want to make subView visible,it is not showing the subView.

    topsubView.bottomAnchor.constraint(equalTo: bottomSubView.topAnchor, constant: 8).isActive = false
    self.view.layoutIfNeeded()
    

    Hope you understand my problem.Thanks in Advance.

  • John
    John about 6 years
    Thanks but i want to remove the space of the subView also
  • Naveed Ahmad
    Naveed Ahmad over 5 years
    why properties are reset by this exension
  • sandpat
    sandpat over 5 years
    What If the views don't have constant spacing?
  • C. Kontos
    C. Kontos over 4 years
    Is the hidden part necessary when you are setting the height constraint to 0?
  • Mukesh Lokare
    Mukesh Lokare over 4 years
    Thats how it work in iOS, gone feature is not available on IOS. Somehow even you remove view from superview then you will have to create that view again then..
  • C. Kontos
    C. Kontos over 4 years
    Well I have similar code without using self.yourView.hidden = true and everything works fine since I am making the view's height constraint equal to 0 when I want it hidden.
  • Mukesh Lokare
    Mukesh Lokare over 4 years
    Yes, thats true, But the best practices in the iOS is we should have to make view hidden as well.
  • Pelanes
    Pelanes over 3 years
    This is the quickest and easiest solution, but maybe is not the best for all cases! @sandpat you just need to set the view inside stack with no fixed size, and two constraints for the top and bottom (or lead and trail) of the views inside this view.
  • Ashraf Amin
    Ashraf Amin about 2 years
    Bravo tqvm. Worked for iOS newbie like me. Thumbs up !