Search code examples
iosswiftuialertcontroller

Set numberOfLines for specific UIAlertController (not all of them)


I was having issues with text truncating with UIAlertController but that issue is fixed now with:

UILabel.appearance(whenContainedInInstancesOf: [UIAlertController.self]).numberOfLines = 2

This will set ALL UIAlertControllers to numberOfLines = 2 but I only want this set to one specific UIAlertController. How can I achieve this?

My actual code for the UIAlertController:

let sheet = UIAlertController(title: "Location", message: nil, preferredStyle: .actionSheet)
    
for data in locationListArr {
    let displayName = "\(data.locAddr1 ?? "") \(data.locAddr2 ?? "") \(data.locCity ?? "") \(data.locProv ?? "") \(data.locPC ?? "")".uppercased()
    
    let item = UIAlertAction(title: displayName, style: .default) { (action) in
        self.locID = data.locID
        self.locName = data.locName ?? ""
        self.locAddr1 = data.locAddr1 ?? ""
        self.locAddr2 = data.locAddr2 ?? ""
        self.locCity = data.locCity ?? ""
        self.locProv = data.locProv ?? ""
        self.locPC = data.locPC ?? ""
        
        self.locationBtn.setTitle(displayName, for: .normal)
    }
    sheet.addAction(item)
}

present(sheet, animated: true, completion: nil)
UILabel.appearance(whenContainedInInstancesOf: [UIAlertController.self]).numberOfLines = 2

Solution

  • Apple's docs say you shouldn't subclass UIAlertController, but I just tried it, and you can create an empty subclass of UIAlertController, and use UILabel.appearance(whenContainedInInstancesOf:) on that subclass.

    Take this example:

    // Define a dummy subclass of `UIAlertController`
    class FooController: UIAlertController {
    }
    
    class ViewController: UIViewController {
        
        let buttonLabels = [
        """
        Button1
        line2
        """,
        """
        Button2
        line2
        """,
        """
        Button3
        line2
        """
        ]
    
        @IBAction func handleAlertButton(_ sender: Any) {
            // Invoke a normal UIAlertController
            presentAlert(type: UIAlertController.self)
        }
        
        @IBAction func handleFooButton(_ sender: Any) {
            // Invoke a FooController.
            presentAlert(type: FooController.self)
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Tweak the appearance of UILabels in `FooController`s
            UILabel.appearance(whenContainedInInstancesOf: [FooController.self]).numberOfLines = 2
        }
        
        // This function invokes either a normal `UIAlertController` or a custom subclass of `UIAlertController`.
        func presentAlert(type: UIAlertController.Type) {
            let sheet = type.init(title: type.description(), message: nil, preferredStyle: .actionSheet)
                
            for buttonTitle in buttonLabels {
                
                let item = UIAlertAction(title: buttonTitle, style: .default) { (action) in
                    print("Button \(buttonTitle) tapped")
                }
                sheet.addAction(item)
            }
    
            present(sheet, animated: true, completion: nil)
        }
    

    The handleFooButton() IBAction creates a UIAlertController with multi-line action titles, but the handleAlertButton() IBAction creates a normal UIAlertController where the actions have single-line titles. That proves that the line UILabel.appearance(whenContainedInInstancesOf: [FooController.self]).numberOfLines = 2 only sets the appearance of UILabels in the context of the dummy FooController. It does not prove that there are no adverse side-effects of ignoring the docs and subclassing UIAlertController.

    Given that the subclass of UIAlertController is completely empty, and only defined in order to give a custom target to UILabel.appearance(whenContainedInInstancesOf), this may be safe. I say "may" because Apple explicitly says not to subclass UIAlertController, and you ignore explicit instructions like that from the docs at your own risk.

    Given that risk, you are probably better off creating your own modal view controller that looks and acts like a UIAlertController (As matt suggests in his comment.) It's not that complicated.