Search code examples
swiftsubclassviewdidloadlayoutsubviews

UIButtons setAttributedTitle Disappears After Calling layoutSubviews in UIButton Extension


I had a corner radius problem with all of my UIButtons, but was finally able to resolve the issue by following the solution at (Setting corner radius through viewDidAppear() or viewWillLayoutSubviews()? ) However, now I have "lost" all of my attributed button titles. I have been battling this issue for a couple of weeks and feel like I am very close to figuring it all out. I apologize if this is a poorly phrased question as I am still relatively new to Swift.

Here is the application I am working on: my default iOS calculator project

In my Swift-file, UIButtonExtension.swift, I have the following:

import Foundation
import UIKit

extension UIButton {
   override open func layoutSubviews() {
        super.layoutSubviews()

        let radius = min(bounds.width, bounds.height) / 2
        layer.cornerRadius = radius

    }
}

In one of my other Swift-files, myCalculatorViewController.swift, I have:

import UIKit

class myCalculatorViewController: UIViewController {

@IBOutlet weak var tag1_Button: UIButton!
@IBOutlet weak var tag2_Button: UIButton!
@IBOutlet weak var tag3_Button: UIButton!
//     .....
@IBOutlet weak var tag19_Button: UIButton!


override func viewDidLoad() {
super.viewDidLoad()

// main/basic calc button view controller

tag1_Button.titleLabel?.textAlignment = .center
tag1_Button.contentHorizontalAlignment = .center
tag1_Button.titleLabel?.numberOfLines = 1
let str_tag1_Button = NSMutableAttributedString(string: "AC")
str_tag1_Button.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 10), range: NSMakeRange(0, 2))    
str_tag1_Button.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0), range: NSMakeRange(0, 2))
tag1_Button.setAttributedTitle(str_tag1_Button, for: .normal) 
tag1_Button.backgroundColor = UIColor(red: 40/255.0, green: 247/255.0, blue: 45/255.0, alpha: 1.0)

// and so forth with the rest of the tag buttons up to tag19_Button that sets title, "="

}
}

Next, in another Swift-file, instantiatedLandscapeButtonViewController, I have a similiar setup for the UIButtons with tag1 to tag30:

import UIKit

class instantiatedLandscapeButtonViewController: UIViewController {


@IBOutlet weak var tag1_Button: UIButton!
@IBOutlet weak var tag2_Button: UIButton!
@IBOutlet weak var tag3_Button: UIButton!
//     .....
@IBOutlet weak var tag30_Button: UIButton!

override func viewDidLoad() {
super.viewDidLoad()

// additional landscape buttons, setup

tag1_Button.titleLabel?.textAlignment = .center
tag1_Button.contentHorizontalAlignment = .center
tag1_Button.titleLabel?.numberOfLines = 1
let str_tag1_Button = NSMutableAttributedString(string: "mc")
str_tag1_Button.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 10), range: NSMakeRange(0, 2))
str_tag1_Button.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor(red: 251/255.0, green: 251/255.0, blue: 251/255.0, alpha: 1.0), range: NSMakeRange(0, 2))
tag1_Button.setAttributedTitle(str_tag1_Button, for: .normal)

// and so forth

}




}

Solution

  • Your code as it stands is dangerous and illegal:

    extension UIButton {
       override open func layoutSubviews() {
    

    No! You cannot perform an override in an extension! The results can be unpredictable. The way to achieve a reliable circular button is to subclass UIButton, where override is legal, and use that subclass.

    class MyCircularButton: UIButton {
        override open func layoutSubviews() {
            super.layoutSubviews()
            let radius = min(bounds.width, bounds.height) / 2
            layer.cornerRadius = radius
        }
    }
    

    Now use a MyCircularButton instead of a plain UIButton wherever you want a circular button. I don't know that this will solve the issue you're having, but it is certainly a required first step.

    I tested your remaining code like this, and it worked fine:

    class ViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            let tag1_Button = MyCircularButton()
            tag1_Button.frame = CGRect(x: 100, y: 100, width: 40, height: 40)
            tag1_Button.titleLabel?.textAlignment = .center
            tag1_Button.contentHorizontalAlignment = .center
            tag1_Button.titleLabel?.numberOfLines = 1
            let str_tag1_Button = NSMutableAttributedString(string: "AC")
            str_tag1_Button.addAttribute(.font, value: UIFont.systemFont(ofSize: 10), range: NSMakeRange(0, 2))
            str_tag1_Button.addAttribute(.foregroundColor, value: UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0), range: NSMakeRange(0, 2))
            tag1_Button.setAttributedTitle(str_tag1_Button, for: .normal)
            tag1_Button.backgroundColor = UIColor(red: 40/255.0, green: 247/255.0, blue: 45/255.0, alpha: 1.0)
            self.view.addSubview(tag1_Button)
        }
    }
    

    Result:

    enter image description here