I have UITableView Cell in which 4 UIViews are arranged in a vertical order like this,
In each UIView, I have two Labels like this,
I have taken a IBOutlet for Height Constraint of all of the Four UIViews and I want to make the height 0 of the view when data is not available in that view. The problem is View is not getting 0 height due to the 10 px bottom constraint of UILabel inside that View.
What i am getting is like this,
Code where constraints are handled
NSString *toName = [self jointNameFromArray:evidence.arrTo];
if ([Utility isEmptyString:toName]) {
cell.toViewHeight.constant = 0;
}
else {
cell.lblToName.text = toName;
}
NSString *fromName = [self jointNameFromArray:evidence.arrFrom];
if ([Utility isEmptyString:fromName]) {
cell.fromViewHeight.constant = 0;
}
else {
cell.lblFromName.text = fromName;
}
NSString *ccName = [self jointNameFromArray:evidence.arrCc];
if ([Utility isEmptyString:ccName]) {
cell.ccViewHeight.constant = 0;
}
else {
cell.lblCCName.text = ccName;
}
NSString *custName = [self jointNameFromArray:evidence.arrCustodian];
if ([Utility isEmptyString:custName]) {
cell.custViewHeight.constant = 0;
}
else {
cell.lblCustName.text = custName;
}
[cell layoutIfNeeded];
If you're targeting iOS 9+, I'd suggesting using UIStackView, as it does exactly what you want just by setting the .hidden property of your view to true.
https://www.raizlabs.com/dev/2016/04/uistackview/
If you are unable to use UIStackView, you will need to set one of the vertical padding constraints to a lower priority than the height constraint. You will also need to set .clipsToBounds to true.
Set constraint programatically and write logic as u want it is good idea and dont forgot to set layoutifnedded.
Related
I have a custom UITableViewCell with UITextView, UICollectionView and UITableView inside. They have a 10 px space between. In some cases I have only UITextView, sometimes only collection view etc.
When no text - I have to move UICollection view up by 10 px. And If I have no text, tableView and collectionView it should be zero height.
- (void)configureMiddleWrapperState
{
self.middleWrapperHasNote = self.noteTextView.text.length > 0;
self.noteTextViewTopConstraint.constant = self.middleWrapperHasNote ? 7.f : 0.f;
self.staffWrapperTopConstraint.constant = self.middleWrapperHasStaff ? 7.f : 0.f;
self.tagsWrapperTopConstraint.constant = self.middleWrapperHasTags ? 7.f : 0.f;
BOOL middleWrapperHasAtleastOne = self.middleWrapperHasNote || self.middleWrapperHasStaff ||
self.middleWrapperHasTags;
self.middleWrapperLastTenPixelSpaceConstraint.constant = middleWrapperHasAtleastOne ? 7.f : 0.f;
[self setNeedsUpdateConstraints];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self setExcludedPaths];
[self configureMiddleWrapperState];
}
The result isn't like expected:
instead of one line.
When only text is looks like expected.
Constraints become work only if I scroll up and down many times.
try add
[self setNeedsLayout];
after
[self setNeedsUpdateConstraints];
I've found the issue. Coz my self.middleWrapperHasTags doesn't set correctly when reuse.
I'm new to auto layout constraints. In my application there are 3 buttons in one line. Now I want to set the constraints programatically as shown in the image below:
If there is one, then the button show in center horizontally.
If two buttons are enable, then second line shown in same image as three image.
My approach is to setting all buttons centerX to superview's centerX at the beginning.
IBOutletCollection(UIButton) NSArray *allButtons;
//3 buttons' references
IBOutletCollection(NSLayoutConstraint) NSArray *allButtonCenterConstraints;
//buttons' centerX constraints' references
and then if in viewDidAppear:
int t = arc4random() %3;// one of the 3 cases you will need
if (t == 0) {//if you want all 3 buttons appears
//all buttons shown
NSLayoutConstraint *c1 = allButtonCenterConstraints[0];//first button's centerX reference.
NSLayoutConstraint *c2 = allButtonCenterConstraints[2];//third button's centerX reference.
c1.constant = -self.view.frame.size.width*0.25;//push left
c2.constant = +self.view.frame.size.width*0.25;//push right
}
else if (t == 1)
{
//two buttons shown;
[allButtons[0] setHidden:YES];// close the one you dont need
NSLayoutConstraint *c1 = allButtonCenterConstraints[1];
NSLayoutConstraint *c2 = allButtonCenterConstraints[2];
c1.constant = -self.view.frame.size.width*0.125;
c2.constant = +self.view.frame.size.width*0.125;
}
else
{
//close 2 buttons
[allButtons[0] setHidden:YES];
[allButtons[1] setHidden:YES];
}
[self.view layoutIfNeeded];
if you need something depends on the width of buttons, you can set the constants according to the width of buttons.
Make constraint outlet after that you set constraint pragmatically
like as :
#IBOutlet var mainViewWidthConstant: NSLayoutConstraint!
if (self.view.frame.size.width == 320){
mainViewWidthConstant.constant = 320
}
else if (self.view.frame.size.width == 375)
{
mainViewWidthConstant.constant = 375
}
else{
mainViewWidthConstant.constant = 414
}
I want to move background to the left like my character is walking and show number of walking but it can't do that at the same time. this method leftfootButton can only set text in label
- (IBAction)leftfootButton:(id)sender {
_bgGround.center = CGPointMake(_bgGround.center.x -50, _bgGround.center.y);
i = i+1;
self.show.text = [NSString stringWithFormat:#"%d",i];
}
If i comment the code that sets the text out then it can move background
- (IBAction)rightfootButton:(id)sender {
_bgGround.center = CGPointMake(_bgGround.center.x - 50, _bgGround.center.y);
i = i+1;
//self.show.text = [NSString stringWithFormat:#"%d",i];
}
what should i do?
You should not change frames directly when you use auto layout because layoutSubviews method will return frames to their previous position. This method is called by system in many cases. You could override it to set the frame rectangles of your subviews directly. But I recommend you to change constraints of your background view. Even if you did not set constraints to your views, they was added automatically.
When you use Layout Constraint then you should never change the frame, you should try to change the constraint, e.g.
Create a IBOutlet for x contraint for the view like below, make sure it's correctly connected.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *xContraint;
Then in your action
- (IBAction)leftfootButton:(id)sender {
self.xContraint.constant -= 50;
i = i + 1;
self.show.text = [NSString stringWithFormat:#"%d", i];
}
So I have setup a UIView that contains a UIScrollView (and child content view) that has sub views that are series of UILabels and UIViews that grow and shrink depending on the content contained in them, all using AutoLayout from the Storyboard. This works when I have something like Label - Label - Label - View w/o any issues, however if I put an empty UIView in-between two labels and insert sub views on the UIView, I'm not seeing the results I'm expecting. I have the following layout in a storyboard:
...where the teal and blue views are labels that grow to infinite height and the orange view (optionsPanel) is an empty UIVIew that I later inject sub views into. The rest of the stuff on the window is UILabels and UISegment controls. Between each row of views I have a Vertical Space constraint with a constant of 8. This all worked beautifully until I had to put in the empty UIView and programmatically inject sub views. The code I would expect to work would be something like (optionsPanel is the orange colored UIView)...
optionsPanel.translatesAutoresizingMaskIntoConstraints = YES;
NSArray *options = [product objectForKey:#"options"];
lastTop = 10;
for(int i=0;i<options.count; i++) {
NSDictionary *option = [options objectAtIndex:i];
NSArray *values = [option objectForKey:#"values"];
if([self hasNoneValue:values] && values.count == 2) {
NSDictionary *value = [self notNoneValue:values];
M13Checkbox *optionCheck = [[M13Checkbox alloc] initWithTitle:[option objectForKey:#"name"]];
optionCheck.frame = CGRectMake(0, lastTop, 280, 25);
[optionsPanel addSubview:optionCheck];
lastTop += 25;
} else {}
}
...where the orange UIView would magically grow and everything would just get pushed around accordingly, however this is what I'm seeing:
...the orange UIView does not grow at all, and the other two top UIView have gone somewhere off the screen. So my next guess was to turn off the Autoresizing Mask using...
optionsPanel.translatesAutoresizingMaskIntoConstraints = NO;
...but I'm getting a result where everything appears to be working but the orange UIView (optionsPanel) has no height for whatever reason and looks like:
This is getting closer to what I would expect, so I thought I would force the height of the orange UIView using code like...
frame = optionsPanel.frame;
frame.size.height = lastTop;
optionsPanel.frame = frame;
...but this appears to have no affect on anything.
Purely guessing, I found that this code almost works, if I arbitrary set the optionPanel's origin to something much larger than the space that is needed....
optionsPanel.translatesAutoresizingMaskIntoConstraints = YES;
NSArray *options = [product objectForKey:#"options"];
lastTop = 10;
for(int i=0;i<options.count; i++) {
NSDictionary *option = [options objectAtIndex:i];
NSArray *values = [option objectForKey:#"values"];
if([self hasNoneValue:values] && values.count == 2) {
NSDictionary *value = [self notNoneValue:values];
M13Checkbox *optionCheck = [[M13Checkbox alloc] initWithTitle:[option objectForKey:#"name"]];
optionCheck.frame = CGRectMake(0, lastTop, 280, 25);
[optionsPanel addSubview:optionCheck];
lastTop += 25;
} else {}
}
lastTop += 10;
frame = optionsPanel.frame;
frame.size.height = lastTop;
frame.origin.y += 300; //some arbitrarily large number
optionsPanel.frame = frame;
..which gives this result:
...but apparently the AutoLayout has decided that the name label needs to take up the extra space. Its an ugly approach but if I could figure out how much space I need then I could just push everything down, if I had to. What's the secret to having a dynamic UIView between two dynamically sized labels and everything just work???
As #Timothy says, you need to manually add constraints to the subviews of the orange view if you want it to resize based on its contents—views don’t do this by default.
In general, if you’re using autolayout in a window, you should never be manually setting the frame of any view. Autolayout overrides any frames you set the every time it’s called, so even if you manage to manually get it working for a second it’ll fail the next time anything triggers a layout.
For views created in code, it's perfectly fine to set their frames as long as their translatesAutoresizingMaskIntoConstraints property is YES (the default, by the way).
However, for a view instantiated in storyboard or a nib, you can not set its translatesAutoresizingMaskIntoConstraints to YES.
I have two views one is textView and below it is a scrollview, in my application the textView should toggle expand when touch it.I set the "Top Space to: Text View" for scrollView in IB.But When I expand the textView it seems not work.
here is the toggle expand code.
- (void)onTextViewClicked:(id)sender
{
CGRect targetFrame = _descTextView.frame;
if (_isTextViewExpand) {
targetFrame.size.height = _descTextViewNormalHeight;
_descTextView.frame = targetFrame;
_isTextViewExpand = NO;
[_contentScrollView setContentSize:CGSizeMake(320.f, kContentScrollViewDefaultHeight)];
}
else {
targetFrame.size.height = _descTextViewExpandHeight;
_descTextView.frame = targetFrame;
_isTextViewExpand = YES;
[_contentScrollView setContentSize:CGSizeMake(320.f, kContentScrollViewDefaultHeight + ( _descTextViewExpandHeight - _descTextViewNormalHeight ))];
}
}
You need a different approach entirely. With Auto Layout, you should never call -setFrame:. If you want the views to grow you should add a constraint or edit one of the existing constraints and then call either -setNeedsUpdateConstraints or -layoutIfNeeded.
Or just turn off Auto Layout.