When my colleague asked me the question, a word "elastic box" popped into my head.
Problem:
There are 4 controls arranged in a Cell in a single pane, as shown in the layout:
Assume:
1. These controls are fixed in height and y coordinates.
2. The blue control x position is fixed, but the right side is aligned to the black control.
3, black, red, green control width fixed, right side aligned to the right side of the control (green Control right aligned to the right of the cell).
Requirements:
1. When any of the controls in the black, red, and green controls are hidden, the remaining two controls automatically move to the control that occupies the hidden control, and the blue control automatically fills the remaining width. The following is the effect of hiding one of the controls individually:
2, and so on, when hiding any of these 2 controls and 3 controls all hide the effect as shown:
If it is HTML5, the problem with "elastic box" to solve is more appropriate. However, "elastic box" is a new content in CSS 3.0, IOS does not support the elastic box, we can only solve the problem ourselves.
Fortunately IOS has an automatic layout, and we can use automatic layout to solve this problem (and of course we need a little bit of code).
First, UI design
Open the storyboard, drag 4 uiview into the Viewcontrollerz, and 3 buttons as shown:
This 4 UIView and 3 UIButton are what respectively, I believe you can be at a glance. Button First, take a look at 4 view.
The Automatic layout constraint for blue view is this:
Top:24,leading:16,height:24,trailing:10
The layout constraints for black, red, and green view are the same:
Width:37,height:24,top:24,trailing:10
The four uiview are connected to the following iboutlet, respectively:
Blue V1
Black v2
Red v3
Green V4
Click events for three buttons are connected to three ibaction, respectively:
@IBActionfunc Hidegray (sender:anyobject) {
Hide (v2)
}
@IBAction func hidered (sender:anyobject) {
Hide (V3)
}
@IBAction func Hidegreen (sender:anyobject) {
Hide (v4)
}
The Hide () method is described later.
First, flexible box design
When black, red, green view is hidden (that is, the hidden property is true), it automatically frees up the space it occupies, and we need to make their layout constraints change according to the hidden property.
From the above we can be informed that their automatic layout constraints are mainly the following:
width, height, leading, trailing.
These layouts are closely related to the space occupied by view. Among them, height we do not care, because they when the width=0 when their occupied space has been released, the height of how much is irrelevant.
So that is, when the view is hidden, we let the width, leading, and trailing of the view at the same time be 0, releasing the space occupied by the view.
Therefore, we need to get the three constraints of width, leading, trailing at run time and modify them according to the hidden property. So can we get the specified constraint on the view at run time? The answer is yes.
We know that UIView has a constraints () method that returns an Nslayoutconstraints array that contains all of its width, height is the constrains of the view, and leading, Trailing belongs to Superview. We can find the constraints we want by traversing the two arrays.
We use a uiview extension to achieve this goal:
Extension uiview{
Func widthconstraint ()->nslayoutconstraint? {
For constraint in self.constraints () {
Let FirstItem = Constraint.firstitem as? UIView
if FirstItem = = Self && constraint.firstattribute ==nslayoutattribute.width{
println ("I gotit:\ (constraint)")
return constraint as? Nslayoutconstraint
}
}
return Nil
}
Func leadingconstraint ()->nslayoutconstraint? {
if Self.superview = = Nil {
return Nil
}
For constraint in self.superview!. Constraints () {//This constraint is in Superview
Let FirstItem = Constraint.firstitem as? UIView
Let SecondItem = Constraint.seconditem as? UIView
if FirstItem = = Self && constraint.firstattribute ==nslayoutattribute.leading{
println ("I gotit:\ (constraint)")
return constraint as? Nslayoutconstraint
}
}
return Nil
}
Func trailingconstraint ()->nslayoutconstraint? {
if Self.superview = = Nil {
return Nil
}
For constraint in self.superview!. Constraints () {//This constraint is in Superview
Let FirstItem = Constraint.firstitem as? UIView
if FirstItem = = Self && constraint.firstattribute ==nslayoutattribute.trailing{
println ("I gotit:\ (constraint)")
return constraint as? Nslayoutconstraint
}
}
return Nil
}
}
Then we'll design a flexible box to manage the three view. The main purpose of the Elastic box class is to save the values of the three constraints of the view in one place (for example, in a dictionary) and then restore the constraint to its original value and display it when the hidden property of a view is set to false.
Class flexiblebox:nsobject{
structviewspace:printable{
var widthconstant:cgfloat = 0
var leadconstant:cgfloat = 0
var trailconstant:cgfloat = 0
var description:string {
Return "width-\ (widthconstant) \nleading-
\ (leadconstant) \ntrailing-\ (trailconstant) "
}
}
var cachedconstraints = [Uiview:viewspace] ()
Func addviews (Views:[uiview]) {
For view in views {
AddView (view)
}
}
Func AddView (V:uiview) {
var space = Viewspace ()
If let constraint = V.trailingconstraint () {
Space.trailconstant = Constraint.constant
}
If let constraint = V.leadingconstraint () {
Space.leadconstant = Constraint.constant
}
If let constraint = V.widthconstraint () {
Space.widthconstant = Constraint.constant
}
Cachedconstraints[v]=space
println ("\ (space)")
}
Func Freeviewspace (V:uiview) {
V.widthconstraint ()?. Constant = 0
V.leadingconstraint ()?. Constant = 0
V.trailingconstraint ()?. Constant = 0
}
Func Resumeviewspace (V:uiview) {
Let space = Cachedconstraints[v]?? Viewspace ()
V.trailingconstraint ()?. Constant = Space.trailconstant
V.leadingconstraint ()?. Constant = Space.leadconstant
V.widthconstraint ()?. Constant = Space.widthconstant
}
deinit{
Cachedconstraints.removeall (Keepcapacity:false)
}
}
Second, the use of flexible boxes
Declare a flexible box in the view Controller:
Let FlexBox = Flexiblebox ()
Then in the Viewdidload method:
Flexbox.addviews ([v2,v3,v4])
Then, when you click the button, the following method is called to hide (or unhide) a view:
Func Toggleviewhiddenstatus (V:uiview) {
If V.hidden = = False {
Flexbox.freeviewspace (v)
}else{
Flexbox.resumeviewspace (v)
}
V.hidden =!v.hidden
Self.view.setNeedsLayout ()
}
The last sentence, self.view.setNeedsLayout (), causes all automatic layout constraints to be recalculated.
IOS Auto-layout and elastic box