Problems of constraints: sizeToFit doesn't wor

2019-08-18 15:41发布

问题:

I'm very new to iOS developement, I'm starting to use auto layout and constraint. I come from Web design so some behaviour are very confusing to me. I'm trying to do a vertical menu taking all the page (centered X and Y). I have made a custom class just for this menu. The goal is to create any menu easily when I need it just like that:

myMenu = MyMenu([
            MyMenu.ButtonInfo("Galerie","icon_gallery",onclick:{return}),
            MyMenu.ButtonInfo("Camera","icon_camera",onclick:{return})
]);
myMenu.show()

This code would create and show a big menu taking all the window (with a black 0.5-opacity layout behind it). Here is basically what I want to create:

Illustration of the menu (image):

I want to create everything programmatically. Therefore, I created first a vertical UIStackView, and I add custom UIViews into it (custom class that I call "Menu button". Each "MenuButtom" is an icon on the left and a label on the right. Here is the code of the MenuButton custom view

class MenuButton: UIView {

var labelView = UILabel()
var iconView:UIImageView

public var title:String
public var icon:String
public var callback: ()->Void

init(_ title:String, _ icon:String,_ callback:@escaping ()->Void )
{
    self.title = title
    self.icon  = icon
    self.callback = callback

    self.iconView = UIImageView(image:UIImage(named: icon)!)

    super.init(frame: CGRect.zero)

    self.frame = CGRect.zero
    self.translatesAutoresizingMaskIntoConstraints = false
    self.layoutIfNeeded()

    self.addSubview(iconView)
    self.addSubview(labelView)

    iconView.translatesAutoresizingMaskIntoConstraints = false
    labelView.translatesAutoresizingMaskIntoConstraints = false
    labelView.backgroundColor = UIColor.green


    iconView.trailingAnchor.constraint(equalTo: labelView.leadingAnchor).isActive = true

    labelView.text = title
    labelView.textColor = UIColor.white

    self.backgroundColor = UIColor.purple

// self.heightAnchor.constraint(equalToConstant:50.0).isActive = true
// self.widthAnchor.constraint(equalToConstant:50.0).isActive = true

    self.layer.borderWidth = 1
    self.layer.borderColor = UIColor.red.cgColor

   // this changes nothing so i commented it .. 
   //self.sizeToFit()




}



required init(coder aDecoder: NSCoder) {
    fatalError("This class does not support NSCoding")
}
}

Here is the code that creates the stackview and adds the MenuButtons into it:

func show()
{
    win.addSubview(modalView)
    // code for the 0.5-opacity dark layout behind the menu : this works well 
    modalView.addGestureRecognizer(UITapGestureRecognizer(
        target:self,action : #selector(dismiss)
    ))
    self.modalView.alpha = 0
    UIView.animate(withDuration:0.2){
        self.modalView.alpha = 1
    }

    // i create the vertical stackview : 
    let parentButtons = UIStackView()
    parentButtons.translatesAutoresizingMaskIntoConstraints = false
    parentButtons.distribution = .fillEqually
    parentButtons.axis = .vertical
    parentButtons.alignment = .fill

    parentButtons.sizeToFit() // ***2*** doesnt work ...
    parentButtons.layoutIfNeeded() // ***3*** do we need to call this only once after creating the view or everytime we need it to update?

    win.addSubview(parentButtons)

    // center my uistackview in the middle of the screen
    parentButtons.centerYAnchor.constraint(equalTo: win.centerYAnchor).isActive = true
    parentButtons.centerXAnchor.constraint(equalTo: win.centerXAnchor).isActive = true


    var btn1 = MenuButton("camera", "icon_camera",{ return })
    var btn2 = MenuButton("gallery", "icon_gallery",{ return })

    parentButtons.addArrangedSubview(btn1)
    parentButtons.addArrangedSubview(btn2)

}

And here is the result, not at all what I expected...: Result 1:

I figured out that the two MenuButton were actually overlapped (I don't understand why). Then, logically, I wanted to make the dimensions of the MenuButton such as it can wrap at least all the children it contains. I find the "magic" method in the documentation: .sizeToFit(). But, nothing happen when I uncomment self.sizeToFit in MenuButton class. No purple background and no red border (MenuButton style) So my first question is: I don't understand why the content of my menu button does not expend to fit the max between the label and the icon that it contains when I call self.sizeToFit(). What I want is just like 2 children "div" in a big parent div (equivalent in web) ; here, the parent will automatically resize to fit exactly its children:

<div class="menuButtonEquivalent">
   <div class="icon" style="float:left"><img ... /></div>
   <div class="label"> label ... </div>
</div>

Then, I wanted anyway to try to fix the problem by setting a constraint to the MenuButton: I uncomment the constraints on the width and height in the MenuButton class. Here is the result:

Result with the constraints on ButtonMenu:

So it's a little better, but I have a question here: why does my MenuButton X start at the label and not at the icon. Why it does not wrap all the children in it? Why only the label and not only the icon or the both (what I want).

I have another third question, is it always mandatory to write the verbose ".translatesAutoresizingMaskIntoConstraints = false" every time a child is created, to use autolayout?

I try to do everything programmatically and not using nib editor (I want to know exactly how everything works before using any higher-level tool).

回答1:

THE THEORY BEHIND AUTO LAYOUT CONSTRAINT

According to the iOS swift the (0,0) i.e., x and y are at the top left corner of the screen. While keeping the constraints you must be careful becoz every iphone screen has its own dimensions. In order to meet those needs first of all we need to know the dimension of the screen and based upon that we can set the alignment.

For example:

x=50, y=50, width=z-(x+x) height=a-(y+y)

were a is the height of the screen and z is the width of the screen

While doing practically add a UIview and then set constraints don't forget to add the your images or whatever may be and give them a child relationship. HAPPY CODING!!!!