Search code examples
iosswiftbuttonuiviewcgrect

How to get UIButton frame X position in Swift (UIKit)?


I want to make my blue underline start and end within the red frame. So, I'm trying to get the X position, the starting position of my UIButton. The red one (red is a bg color). How can I do that?

enter image description here

Things I've tried:

promoButton.frame.origin.x

or

promoButton.bounds.origin.x

result: 0.0

promoButton.layer.position.x

This actually move the underline, but it started at the 'P' of 'Promo' instead of the red frame.

Can anyone tell me how to get the frame X position?

Thankyou in advance.


Update:

This is how i put it in the storyboard:

First there is this Tab View

enter image description here

Next, I have the container

enter image description here

Then the Promo button it self

enter image description here

I guess 0.0 is because it's inside a view. but is it possible to calculate X position from the screen tho?


Solution

  • You can do this with auto-layout / constraints, so you don't need to worry about "frames."

    We'll setup the Storyboard like this - note: we are NOT adding the "underline view" in the Storyboard:

    enter image description here

    I've given the "container view" and the buttons different background colors to make it easier to see the framing.

    We'll make @IBOutlet connections to the three buttons:

    @IBOutlet var btn1: UIButton!
    @IBOutlet var btn2: UIButton!
    @IBOutlet var btn3: UIButton!
    

    and we'll connect the Touch Up Inside for all buttons to the same function.

    In viewDidLoad() we'll create and add the underlineView, and give it a Height constraint of 4.0. It looks like you want it about 8-points below the buttons, so we'll also give it a Top constraint relative to the Bottom of the first button (doesn't really matter which button, as we're assuming they are the same).

    Now, we'll add two class properties:

    var underlineLeadingConstraint: NSLayoutConstraint!
    var underlineWidthConstraint: NSLayoutConstraint!
    

    Again in viewDidLoad() we'll create those two constraints, and set them related to btn1.

    When we tap a button, we'll:

    • deactivate those two constraints

    • re-make them related to the tapped button

    • and animate the change

      @IBAction func btnTap(_ sender: UIButton) { // de-activate the underline view constraints underlineLeadingConstraint.isActive = false underlineWidthConstraint.isActive = false

        // re-create the Leading and Width constraints
        //  related to the tapped button
        underlineLeadingConstraint = underlineView.leadingAnchor.constraint(equalTo: sender.leadingAnchor)
        underlineWidthConstraint = underlineView.widthAnchor.constraint(equalTo: sender.widthAnchor)
      
        // activate those new constraints
        underlineLeadingConstraint.isActive = true
        underlineWidthConstraint.isActive = true
      
        // if you want to animate it
        UIView.animate(withDuration: 0.3, animations: {
            self.view.layoutIfNeeded()
        })
      

      }

    Here's the complete controller class:

    class UnderlineViewController: UIViewController {
        
        @IBOutlet var btn1: UIButton!
        @IBOutlet var btn2: UIButton!
        @IBOutlet var btn3: UIButton!
    
        var underlineView: UIView!
        
        var underlineLeadingConstraint: NSLayoutConstraint!
        var underlineWidthConstraint: NSLayoutConstraint!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            underlineView = UIView()
            underlineView.backgroundColor = .blue
            underlineView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(underlineView)
            
            // underlineView height is 4-points
            underlineView.heightAnchor.constraint(equalToConstant: 4.0).isActive = true
            
            // looks like you want it about 8-points below the button bottom?
            underlineView.topAnchor.constraint(equalTo: btn1.bottomAnchor, constant: 8.0).isActive = true
            
            // create Leading and Width constraints for the underlineView
            underlineLeadingConstraint = underlineView.leadingAnchor.constraint(equalTo: btn1.leadingAnchor)
            underlineWidthConstraint = underlineView.widthAnchor.constraint(equalTo: btn1.widthAnchor)
            // activate those constraints
            underlineLeadingConstraint.isActive = true
            underlineWidthConstraint.isActive = true
            
        }
    
        @IBAction func btnTap(_ sender: UIButton) {
            // de-activate the underline view constraints
            underlineLeadingConstraint.isActive = false
            underlineWidthConstraint.isActive = false
            
            // re-create the Leading and Width constraints
            //  related to the tapped button
            underlineLeadingConstraint = underlineView.leadingAnchor.constraint(equalTo: sender.leadingAnchor)
            underlineWidthConstraint = underlineView.widthAnchor.constraint(equalTo: sender.widthAnchor)
            
            // activate those new constraints
            underlineLeadingConstraint.isActive = true
            underlineWidthConstraint.isActive = true
            
            // if you want to animate it
            UIView.animate(withDuration: 0.3, animations: {
                self.view.layoutIfNeeded()
            })
        }
        
    }
    

    Here's how it looks when running:

    enter image description here

    Since we are now using constraints - instead of calculating frame coordinates - the underline width and position will automatically update when the "tab buttons" change... such as on device rotation.