80

I'm developing an app for iOS and I'm using the Storyboard with AutoLayout ON. One of my view controllers has a set of 4 buttons, and in certain circumstances i would like to make the first one disappear.

If I use the setHidden:TRUE method the UIButton become invisible but it still obviously take space in the view, and the result is an "hole" which I've not been able to fill making the remaining UIButton to float towards the top of the main view.

In Android I would have simply used View.GONE instead of View.INVISIBLE, but in iOS I'm stuck with this behaviour and I don't want to believe that the only solution is to manually (yes I mean programmatically) move the remaining elements to the top.

I thought I would have been able to do it setting some sort of Constraint to make everything as automatic as it is in Android but I've had no luck.

Before I turn Autolayout OFF, can someone point me to the right direction?

I'm using the IB, but I'm comfortable with programmatic stuff as well.

UPDATE:

Setting the component height to 0 doesn't help as well.

I tried something like this:

UIButton *b;
CGRect frameRect = b.frame;
frameRect.size.height = 0;
b.frame = frameRect;
Parth Bhatt
  • 19,381
  • 28
  • 133
  • 216
elbuild
  • 4,869
  • 4
  • 24
  • 31
  • How about setting the height of the button to zero? – cahn Jul 26 '13 at 00:55
  • I tried something like this: UIButton * b; CGRect frameRect = b.frame; frameRect.size.height = 0; b.frame = frameRect; No luck :( – elbuild Jul 26 '13 at 01:07
  • I know this is a super old question but regarding your update setting the frame to 0 won't help if you're using autolayout. You have to set the height constraint to 0 – wyu Jan 06 '17 at 21:56

13 Answers13

41

Adding a constraint(NSLayoutAttributeHeight) that sets height of the view to 0 worked for me:

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.captchaView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0]];
Deniz
  • 1,575
  • 1
  • 16
  • 27
  • 11
    Swift version: `self.view.addConstraint(NSLayoutConstraint(item: self.captchaView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0))` – iStar Jan 19 '16 at 08:02
  • 1
    This should have been the answer – Rick Royd Aban Feb 01 '16 at 13:29
  • 2
    What if I don't know actual height of desired view (for ex. container view rendered little bit later)? – A. Petrov May 11 '16 at 09:01
  • Thanks a lot for the awesome answer! For someone may need it. I got an error `Unable to simultaneously satisfy constraints`. while I already had a constraints for the view. I solve it by http://stackoverflow.com/a/38625419/1270901 – Nevin Chen Jul 28 '16 at 00:49
  • @iStar i have used same code but it doesn't work ...https://stackoverflow.com/questions/45454992/remove-white-space-after-hide-views-in-scrollview – Sanjay Mangaroliya Aug 02 '17 at 09:20
  • @Deniz i have used same code but it doesn't work ...https://stackoverflow.com/questions/45454992/remove-white-space-after-hide-views-in-scrollview – Sanjay Mangaroliya Aug 02 '17 at 09:20
  • Awesome answer.Saved my day – Rajitha Perera Sep 25 '17 at 05:29
  • but you may have some padding, spacing for this view – user924 Mar 14 '18 at 13:20
  • p.s. constant height sucks, I use equal height and multiplier, for example to take 25% of parent view, any example for it? – user924 Mar 14 '18 at 13:21
  • Usually I'd use UIStackView, but this works best for cases in which you have to push your views to one side of the container view instead of distributing their spacing. Great alternative answer. – ZShock Oct 04 '18 at 15:33
21

All of answers on this questions are inefficient. Best way to equvailent of Android setVisibility:Gone method on iOS is that StackView first select components then in editor, embed in, Stack View,

connect new stack view with IBOutlet, then:

hidden:

UIView * firstView = self.svViewFontConfigure.arrangedSubviews[0];
        firstView.hidden = YES;

visibility:

UIView * firstView = self.svViewFontConfigure.arrangedSubviews[0];
        firstView.hidden = NO;

as using stack view, all constraints will be keeped!

Document

Ucdemir
  • 2,852
  • 2
  • 26
  • 44
  • Seems like a really good solution actually, but it's worth to note that it needs iOS9+. – user3533716 Oct 14 '16 at 09:13
  • I used your brilliant solution (+1 for you, thanks so much!!) but I connected via IBOutlet directly the view I wanted to show and hide, so I have to type just one line of code! :) – CristinaTheDev Jul 12 '17 at 15:46
  • 1
    This answer actually not correct. Android View.GONE removes view on UI. For example if you set View.GONE a element, views below that wiew will shift up. So your suggestion just hides the view which is equal to View.HIDE. Look at this answer : https://stackoverflow.com/questions/11556607/android-difference-between-invisible-and-gone – emin deniz Dec 18 '17 at 13:32
  • I did not search on android api... But I'm mobile developer, so I know mechanic... In Android with boolean flag view can be viewable or not(Gone,Visible) If it removes from ui it will be hard to add again... but in ios,it in layout but with no spaces.... Moreover There is officialy ios hidden property which take place...UIStackview hidden property like android View.Gone – Ucdemir Mar 29 '18 at 09:37
16

add a height constraint to your view as follows:enter image description here

then make an outlet for the height constraint in your viewController file as follows:enter image description here

& then in your viewDidLoad method change constraint height to 0 as follows:

override func viewDidLoad() {
    super.viewDidLoad()
nsLcButtonHeight.constant = 0
}
Ajinkya Patil
  • 5,518
  • 4
  • 20
  • 22
  • 1
    constant height sucks, I use equal height and multiplier, for example to take 25% of parent view, any example for it? – user924 Mar 14 '18 at 13:21
8

To achieve Androids.GONE functionality on iOS is to use a UIStackView. After that hide the child by position. (Apples documentation)

SWIFT 4:

let firstView = cell.rootStackView.arrangedSubviews[0]
    firstView.isHidden = true // first view gone

It's a table cell example, just put both insides Stack view and get item for GONE the child.

enter image description here

Md Imran Choudhury
  • 9,343
  • 4
  • 62
  • 60
6

What you can do is to group your views under a stack view. Then when you hide a particular view, the remaining views will be shifted automatically to fill the space.

You may want to check out the Apple Documentation on Stack Views: https://developer.apple.com/reference/uikit/uistackview

or online tutorials such as: https://www.appcoda.com/stack-views-intro/

mechdon
  • 517
  • 1
  • 10
  • 23
1

1) If your buttons are placed vertically then you must set the height to 0 and in case of horizontally placed buttons, try setting width to 0 or you can set both to 0.

OR

2) you could try this approach which sets button2 on top of button1:

- (void)viewDidLoad
{
[super viewDidLoad];

UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button1 setTitle:@"hello" forState:UIControlStateNormal];

UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button2 setTitle:@"world" forState:UIControlStateNormal];

[button1 sizeToFit]; [button2 sizeToFit];
[button2 setFrame:CGRectMake(button1.frame.origin.x, button1.frame.origin.y, button2.frame.size.width, button2.frame.size.height)];

[self.view addSubview:button1];
[self.view addSubview:button2];

}
iphondroid
  • 498
  • 7
  • 19
1

https://github.com/tazihosniomar/LayoutManager

i hope it will help you .

poyo fever.
  • 742
  • 1
  • 5
  • 22
1

This question is pretty old but the closet thing I've found is setting additional constraints (so the views around the "gone" view know what to do once it's missing)

A  which you want to be     A
|  after setting B to gone  |
B                           C
|                               
C                               
  1. Set a lower priority (750) constraint from C to A.
  2. Add B's top and bottom constraints (or left and right if you want your view to collapse horizontally) to an NSLayoutConstraint array bConstraints. Do this by:
    1. Control click and drag from the constraint to the ViewController
    2. Change Connection from Outlet to Outlet Collection
    3. For name put bConstraints.
    4. Hit connect. This will create an @IBOutlet var bConstraints: [NSLayoutConstraint]! in your ViewController
    5. To add additional constraints: drag from the constraint in Storyboard to the @IBOutlet variable name
  3. Then hide B

    B.hidden = true
    NSLayoutConstraint.deactivateConstraints(bConstraints)
    
  4. To unhide

    B.hidden = false
    NSLayoutConstraint.activateConstraints(bConstraints)
    

Obviously the more and more views you have the more complex this grows, as you need additional constraints from each view

wyu
  • 1,793
  • 17
  • 33
  • it would be more helpful if you explained why you couldn't implement – wyu Jan 16 '17 at 16:37
  • The second point. I am working in Xamarin though. Still haven't found a solution for this problem. Don't know how you should construct a NSLayoutConstraint array with B constraints. I assume it is done programatically, would be nice to see the code for that. Your method seems to me to be the most reasonable way of tackling this problem, however not easy to implement with just pseudo-code – Gustavo Baiocchi Costa Jan 16 '17 at 17:12
  • I haven't used Xamarin. The way it's done in XCode is: ctrl-click and drag from the constraint to the ViewController (this is similar to how you wold create an IBOutlet in xcode). In the popup that appears (looks similar to https://i.stack.imgur.com/JegLK.png) change the "Connection" drop down menu from `Outlet` to `Outlet Collection`. For additional constraints, ctrl drag directly to the variable name. I can upload sceenshots later today (in ~8 hours) – wyu Jan 16 '17 at 17:43
  • so the NSLayoutConstraint array is the outlet collection, got a bit lost here haha, tks for taking your time to answer my questions :) – Gustavo Baiocchi Costa Jan 17 '17 at 11:58
  • yes, i'll update the answer to be more clear. So the solution is working for you? – wyu Jan 17 '17 at 15:47
  • I am still trying, but finding hard to make the outlet connection in xamarin. The other problem is that I have an UIImageView with center.y connected to the right side of the B view which is the view that I need to hide – Gustavo Baiocchi Costa Jan 24 '17 at 11:07
  • You shouldn't need to involve subview constraints at all. Are you having difficulty selecting the constraint so you can ctrl-drag it? You can ctrl-drag from the document outline if it's easier than having to select the constraint in the interface builder - https://static1.squarespace.com/static/56ddcaef01dbae76cf85000d/t/56fabf1207eaa09cd439d3dc/1459273497363/ – wyu Jan 24 '17 at 15:49
0

As my research has shown, not even AutoLayout can help you. You have to manually replace the views affected by the optionally shown component (in my case, all the views to the bottom of the optional view, but you I am sure you can adapt this to handle all the buttons to the right of your optional button):

- (IBAction)toggleOptionalView:(id)sender {
    if (!_expanded) {
        self.optionalView.frame = CGRectMake(self.optionalView.frame.origin.x, self.optionalView.frame.origin.y, self.optionalView.frame.size.width, _optionalHeight);
        self.bottomView.frame = CGRectMake(self.bottomView.frame.origin.x, self.bottomView.frame.origin.y+_optionalHeight, self.bottomView.frame.size.width, self.bottomView.frame.size.height);
        _expanded = YES;
    } else {
        self.optionalView.frame = CGRectMake(self.optionalView.frame.origin.x, self.optionalView.frame.origin.y, self.optionalView.frame.size.width, 0);
        self.bottomView.frame = CGRectMake(self.bottomView.frame.origin.x, self.bottomView.frame.origin.y-_optionalHeight, self.bottomView.frame.size.width, self.bottomView.frame.size.height);
        _expanded = NO;

    }
}

It is advisable not to hard-code the height/width of the optional components, otherwise your code breaks every time you edit the XIB/Storyboard. I have a field float _optionalHeight which I set in viewDidLoad, so it is always up to date.

Katlu
  • 496
  • 6
  • 14
0

You can also clear the page, or at least certain cells of the page, and redefine the whole thing. It's fast and works well. I didn't write the code but found it in this pre-existing project I'm working on. Create ManagementTableSection and ManagementTableCell classes to manage it all. Sorry I can't provide better defined code.

craned
  • 2,991
  • 2
  • 34
  • 38
0

Building off the answer provided by Deniz, here is a solution using constraints in Swift

For example: If you have 3 views, A_view B_view and C_view vertically aligned in that order and you want to "Hide" B and also adjust the difference, add a constraint

B_view.removeFromSuperView()
var constr = NSLayoutConstraint(item: C_view, 
                                attribute: NSLayoutAttribute.Top, 
                                relatedBy: NSLayoutRelation.Equal, 
                                toItem: A_view, 
                                attribute: NSLayoutAttribute.Bottom,
                                multiplier: 1,
                                constant: 20)
view.addConstraint(constr)

constant is (in this case) the amount of vertical space between C_view and A_view

The4thIceman
  • 3,709
  • 2
  • 29
  • 36
0

I added a new property to a custom UIView implementation called "visible" which, when set to false, adds a constraint to collapse the view (I only added a width constraint since my list is horizontal, but the best way might be to add a height constraint of 0 as well).

var visible:Bool = true{
        didSet{
            if(visible){
                clipsToBounds = false;
                removeConstraint(hideConstraint!)
            }else{
                clipsToBounds = true
                addConstraint(hideConstraint!)
            }
        }
    }

You need to initialize the zero-width constraint on the view and add it as a field:

private var hideConstraint:NSLayoutConstraint?


func someInitFunction(){
    hideConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 0.0)
    ...
}
Anthony De Smet
  • 2,265
  • 3
  • 17
  • 24
-3

setHidden:TRUE/FALSE is the nearest equivalent to Android View.GONE/VISIBLE.

The View not necessarily take space if not visible!

I have made a ComboBox-Alike with a ListView laying on top of other views. I's only visible while selecting:

enter image description here

thpitsch
  • 2,016
  • 2
  • 21
  • 27
  • you forgot that in android we also have view.invisible... view.gone is not similar to setHidden... its view.invisible that is similar to setHidden – Nav Apr 08 '15 at 07:26