Search code examples
iosswiftsafearealayoutguide

UIButton subclass, fill up SafeArea, inset content


I'm getting the hang of using the safe area when using custom UI elements in .xib/.storyboard.

I now have the case where I have a UIButton subclass that's used everywhere throughout the app. Since it's just a subclass (and not a custom class in .xib) I'm not sure how I would update this to fit my needs.

See the following photo: iPhone X button Here the yellow is the UIButton. On 'regular' iPhones this yellow is the bottom of the screen. What I'm trying to achieve is to have the button go all the way to the bottom of the safe area, while still being in the same position (above the safe area).

Normally I would constraint the button to superview.bottom, and in the button's .xib constraint the content (titleLabel, buttonImage, etc) to safearea.bottom.

Since that's not possible here, how would I do that?

I tried adding the constraints programatically in the UIButton subclass, but to not avail.

Example:

if #available(iOS 11.0, *) {
    NSLayoutConstraint.activate([
        (titleLabel?.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor))!,
        (titleLabel?.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor))!
        ])
} else {
    // Fallback on earlier versions
}

Thanks in advance!


Solution

  • You can use the following approach for this,

    1. Create a UIView.
    2. Add a UILabel to the above created view as a subView.
    3. Add a UIButton to the above created view as a subView.

    Apply proper layout constraints to get the desirable UI.

    func addSaveButton() {
        let height: CGFloat = 60 + self.view.safeAreaInsets.bottom //Height based on safe area
    
        //Custom View
        let customView = UIView(frame: CGRect.init(x: 0, y: self.view.bounds.height - height, width: self.view.bounds.width, height: height))
        customView.backgroundColor = #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)
    
        //Save Label
        let label = UILabel()
        label.text = "Save"
        label.textColor = UIColor.black
    
        //Button
        let button = UIButton(frame: customView.bounds)
        button.addTarget(self, action: #selector(onTapSaveButton), for: .touchUpInside)
    
        //Add label, button as subview in customView
        customView.addSubview(label)
        customView.addSubview(button)
        self.view.addSubview(customView)
    
        customView.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        button.translatesAutoresizingMaskIntoConstraints = false
    
        //Add constraints
        NSLayoutConstraint.activate([
            self.view.leadingAnchor.constraint(equalTo: customView.leadingAnchor),
            customView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            customView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            customView.heightAnchor.constraint(equalToConstant: height),
    
            label.topAnchor.constraint(equalTo: customView.topAnchor, constant: 10),
            label.trailingAnchor.constraint(equalTo: customView.trailingAnchor, constant: -10),
    
            button.topAnchor.constraint(equalTo: customView.topAnchor),
            button.leadingAnchor.constraint(equalTo: customView.leadingAnchor),
            button.trailingAnchor.constraint(equalTo: customView.trailingAnchor),
            button.bottomAnchor.constraint(equalTo: customView.bottomAnchor)
            ])
    
    }
    
    @objc func onTapSaveButton() {
        print("Save button pressed")
    }
    

    In iPhone-X

    enter image description here

    In iPhone-8

    enter image description here

    Approach 2:

    You can follow a bit more simplified approach by playing with the button's titleEdgeInsets.

    func addSaveButton() {
        let height: CGFloat = 60 + self.view.safeAreaInsets.bottom
    
        //Button
        let button = UIButton(frame: CGRect.init(x: 0, y: UIScreen.main.bounds.height - height, width: UIScreen.main.bounds.width, height: height))
        button.setTitle("Save", for: .normal)
        button.backgroundColor = #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)
        button.contentHorizontalAlignment = .right
        button.contentVerticalAlignment = .top
        button.titleEdgeInsets.top = 10
        button.titleEdgeInsets.right = 10
        button.addTarget(self, action: #selector(onTapSaveButton), for: .touchUpInside)
    
        self.view.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
    
        //Add constraints
        NSLayoutConstraint.activate([
            self.view.leadingAnchor.constraint(equalTo: button.leadingAnchor),
            button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            button.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            button.heightAnchor.constraint(equalToConstant: height)
            ])
    }
    

    You can do the same thing in storyboard/subclassing easily. I think this one is better than the previous one.

    Approach 3:

    Subclass UIButton and use it to create your button programatically.

    class CustomButton: UIButton {
        override func draw(_ rect: CGRect) {
            self.contentHorizontalAlignment = .right
            self.contentVerticalAlignment = .top
            self.titleEdgeInsets.top = 10
            self.titleEdgeInsets.right = 10
        }
    }