So I am trying to implement some feature restrictions based on permissions in an app. The feature in question works on a button that is placed at the bottom of the VC in main.storyboard.
There is another button that I want to remain there. They are aligned horizontally, but not in a horizontal stack view. They have an equal-width constraint and together take up the whole vertical space at the bottom.
I want to hide the first one, and thus have the second one take up that entire horizontal space from left to right, but only when the access to the first button is supposed to be restricted. I've tried doing this in viewNeedsRefresh:
let equalWidthConstraint = firstButton.superview?.constraints.filter{
$0.firstItem as? UIButton == firstButton && $0.secondItem as? UIButton == secondButton
}.first
equalWidthConstraint?.isActive = false
firstButton.isHidden = true
secondButton.frame = CGRect(x: 0, y: firstButton.frame.origin.y, width: self.view.frame.width, height: firstButton.frame.height)
But all this does is hide the first button, while the second button is suddenly partially off-screen. If I try without manually disabling the constraint, I get it looking cleaner but the second button is still in its original position, likely because it still maintains the original width due to the constraint.
Personally, in my opinion, I'd do the following:
remove the equal widths constraint
set a width constraint on one of the buttons set to 0.5x the superview width, then create an outlet for the constraint
set the other button with a trailing or leading (depending on the button you chose) to zero.
When you need to collapse the button, you can now set the constraint's constant directly like so:
buttonWidth.constant = 0
That would hide the view and give you the desired effect.
It turns out they built a re-usable component for this in the past before I joined, but did not actually use it more than once. I have converted the page to use that component instead. Sorry guys.
Related
I'm shipping my app for all iPhone models (iOS 10.0+) and have made sure that in all localisations, labels and controls will not overlap.
In a simple static cell, it would be enough to give the label on the left side a leading constraint and center it vertically. The same would be done with the control on the right, but with a trailing constraint. All would be fine.
Until Xcode warning.
It asks for missing trailing or leading constraints to avoid overlapping in any case (which would not happen in mine).
Here is a simple test case:
I silenced the warning by giving a switch on the right a leading constraint of 10.0, just to make sure. It works fine. (Attached image first row)
The same, however, with a segmented control would stretch it all the way to the left to the right side of the label. Attached image 3rd row) Even if I increased the size of the label further to the right it would stomp the width of the label.
Since I'd like to have the cell as in the 2nd row, I did what I considered hacky in the view controller, in which case I'd have to specify exact x values for each screen size (which is ok, but I'd like to avoid) (Storyboards, after all...):
#IBOutlet weak var mySegmentedControl: UISegmentedControl!
override func viewWillLayoutSubviews() {
mySegmentedControl.frame = CGRect(x: 238.0, y: mySegmentedControl.frame.minY,
width: 121.0, height: mySegmentedControl.frame.height)
}
Is there a better way to achieve this?
New versions of Xcode will show this as a warning.
You could fix this Adding greater than or Equal constraint to UILabel's trailing.
NB: you can quick fix by clicking on the Yellow Right Arrow near the My Table View Controller Scene text
The UILabel is variable in length. When you set text to label, it will resize automatically. If you are not setting the Trailing Constraint it may overlaps other views (in this case - segmented control). It will work if you add fixed constraint, but new Xcode shows it as warning. So we have to change it as greater or lesser than constraints.
I'm stuck with a strange bug with Auto Layout.
I have two buttons (confirm and delete) side by side, in the same view, each taking a width that's 50% of their superview, and in some situations (when test = true), I want to hide confirm button so delete button takes all the width of the screen.
Here is my autolayout:
Confirm button:
Delete button:
Proportional width is 50% (1:2) of the superview. Now here is my call, happening in viewWillAppear:
if test {
self.confirmButtonWidthConstraint.constant = 0
self.deleteButtonWidthConstraint.constant = (self.deleteButton.superview?.frame.width)!
}
However, after doing so here is the result:
And after checking the UI debugger, I can see that oddly, this delete button now has a width of... 480, with a starting x of -160. So I don't really know what's happening here. Please help!
May I suggest a different tactic?
Embed your buttons in a stack view (UIStackView) with the buttons set to fill equally.
You can then set your button to disappear with button.isHidden = true. The stack view will handle the layout for you gracefully.
You've set the width proportionally but are updating the constraints constant property. This will need lead to the result you desire as the proportional width will simply add the constant value to the multiplier result.
Instead you should set the multiplier property of your constraints. For example:
if test {
self.confirmButtonWidthConstraint.multiplier = 0.0
self.deleteButtonWidthConstraint.multiplier = 1.0
}
I am using JSQMessagesViewController and I created a custom cell according to this answer.
How to add custom view to JSQMessagesViewController cell so that it includes one view with some buttons and text view?
Now I am struggling to set auto layout constrains correctly. So, the message bubble not display correctly. Please help me set the constraints correctly
So lets take a stab at this. So first things that I would do it set a constraint for the first button to be equal hight of the other. You can accomplish a couple of ways but I will only describe one here for brevity. You can do hold the control button on your computer and select button1 and drag to button2. This will present you with a couple of options that look like this.
You want to select Equal Heights this will make it so both your buttons have the same hight. Then you will want to give it a hight. Once again hold down the control button on your keyboard but this time click button1 drag and release within button1. you should get something along the lines of this.
If you do not get the desired options try dragging in a diagonal direction. Xcode is tying to guess what constraints you want based on the direction of your drag. I.E. If you drag vertically you should get the Height option.
Then you can go to the properties inspector on the right and set a number for how high you would like. Text is normally around 12pt so I would go with about 30pt or more for the hight of a button. Then add a constraint for spacing between them and leading and trailing to the containing view or you could give them a standard width and center them in the view. Which ever fits best for you.
Edit:
You should also adjust the bubble size calculation.
It can be found in the JSQMessagesBubblesSizeCalculator class.
Eg: In the
- (CGSize)messageBubbleSizeForMessageData {
if([messageData isOptionMessage]){
// add button height also (In this case i have set constant 200. But we have to calculate the actual button heights instead)
finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets + 200)
} else {
finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets)
}
}
Let me know if you need any more help and keep up the good work. 👍🏼 🚜
I have in my app one UITextField on the left and one UIButton on the right. The textfield is anchored on the left at the superview (a container view) and in the right to the button.
So in the left of textfield there is a
leading space = 0 in relation of container
and on the right a
trailing space = 0 in relation of button
but if I move the button on the right way, changing the x origin value, why the textfield don't enlarge its width?
(obviously the button has its constraints about width and height and for position, but not that lock the textfield)
so if I do this
self.mybutton.frame = CGRectMake(self.mybutton.frame.origin.x+100, self.mybutton.frame.origin.y, self.mybutton.frame.size.width, self.mybutton.frame.size.height);
the button moved in the right direction but the textfield seems to doesn't enlarge its width,.
Do you know why?
Working with both Auto Layout and programmatic positioning/sizing can create a lot of headaches. Part of this is because you have created constraints in Auto Layout, which are basically "rules" that your app must follow when laying out all of it's views, and when you change the frame, bounds, or center properties you may be invalidating those rules. But since Auto Layout is not constantly recalculating the layout of your views, problems may go unnoticed until a layout recalculation is triggered.
So to answer your question, changing the frame of the button does not change the text because Auto Layout has no idea that anything has changed. Plus you haven't changed the constraints on the button so if you did call - (void) setNeedsUpdateConstraints on your text field and button, the change you are looking for won't happen. The button will move back to it's initial position, the one you set with constraints.
What you may want to do is create an IBOutlet on whatever is controlling how the button gets positioned on the x-axis (i.e. its trailing space...if that is what you are using). Then instead of doing:
self.mybutton.frame = CGRectMake(self.mybutton.frame.origin.x+100, self.mybutton.frame.origin.y, self.mybutton.frame.size.width, self.mybutton.frame.size.height);
You could do something like:
self.mybuttonXconstraint.constant = self.mybuttonXconstraint.constant + 100
[self.parentView setNeedsUpdateConstraints]
The second line is to ensure that Auto Layout knows a constraint has been changed and that it should recalculate the layout for any views involved with the parent's constraints.
This may be of interest to you as well - iOS Developer Library - Auto Layout Guide
Could you try animating the button's trailing constraint?
Like so (I changed the constraint inside an animation block for illustration purposes):
UIView.animateWithDuration(
5.0,
animations: {
self.buttonTrailingMarginConstraint.constant = 0
self.view.layoutIfNeeded() // Necessary when changing constraints.
}
)
Final result:
Git clone project: https://github.com/backslash-f/animating-constraints
I have the following UIButtons and I'm trying to get the red "Start" button to elongate and cover the blue button when tapped:
I plan to have the Start button elongate and then hide the Save button. However, when I try to do this, the start button elongates towards the right.
I have the buttons set up with constraints in IB so I'm changing the constraint in the animation block:
UIView.animateWithDuration(1.0, animations: {
self.toggleButtonWidth.constant *= 2 //toggleButtonWidth is the width constraint of the red button
self.view.layoutIfNeeded()
})
However, this causes the red button to elongate to the right and off screen:
I want to elongate to the left.
PS: I know the buttons and their layout doesn't look pretty, I'm still trying to figure out functionality before doing design
Pin the start button on the right (trailing space to container). With the right edged pinned, the only place the button can "grow" is to the left.
Another thing you can try is getting rid of the width constraint, pinning the left side of the start button (leading space) to the container (i.e. NOT to the save button), and then reducing the constant of that constraint in the animations closure. Something like
leadingConstraint.constant = leadingConstraint.constant - saveButton.bounds.size.width
It's a little hard to decide what the best route is without knowing how you've laid out your auto layout constraints.
Whatever you do, you will probably need to reduce the alpha of the save button. Otherwise the save button may still be visible when the start button "covers" it. You could also do this in the animations closure by adding a statement like saveButton.alpha = 0 Lowering its alpha just makes it invisible, however, so you may need to hide it completely (button.hidden = true) so that the user can't accidentally still tap it.