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).