Search code examples
iosstringswiftuibuttonnsattributedstring

Swift - Attributed strings in buttons


I have some UIbuttons in my app that have a number indicator before the text. Right now, I am just using string interpolation to display the number before the string as seen below.

fruitButton.setTitle("\(fruitCounter) Fruits", forState: UIControlState.Normal)

I need the number to stand out more, rather than just blending in with the title text. Something as simple as a circle surrounding it will do the trick, as seen in the design below: I need my buttons to have a styled number indicator like in the photo. .

I did some research on Attributed Strings in Swift. However, I am just seeing many examples on changing text properties. EG - changing the text color and size of the number indicator. I can't figure out how to add a circle behind.

I do not want this circle to be an image, simply for scalibility purposes. For instance, if that number ends up being 2 digits long, I need the circle to stretch to oval. My thought was using a small view behind the number, and then just applying a color / alpha / radius to achieve the look I need.

So to wrap this up: How can I add circles behind my number indicators using Attributed Strings in Swift?


Solution

  • You should create a UIView subclass that will contain these two elements. Create a corresponding .xib file that includes those entities or implement those entities in code. You can also implement touchesBegan so that this view can act like a button OR add a button instead of a text label and implement a protocol to fire every time the button is hit. I've started this for you with some semi-arbitrary numbers. You'll have to play with them to get them just right.

    class UICoolButton: UIView {
    
        var labelText: NSString?
        var circledNumber: Int?
        var circleSubview: UIView?
    
        init(frame: CGRect, labelText: NSString, circledNumber: Int) {
            super.init(frame: frame);
            self.labelText = labelText;
            self.circledNumber = circledNumber;
            self.layer.cornerRadius = frame.height/3
            self.clipsToBounds = true
            addCircledNumber()
            addTextLabel(labelText)
        }
    
        func setColors(numberColor:UIColor, backgroundColor: UIColor) {
            self.backgroundColor = backgroundColor
            self.circleSubview?.backgroundColor = numberColor
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame);
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder);
        }
    
        func addTextLabel(text: NSString) {
            let origin = CGPoint(x:self.frame.width * 0.4, y:self.frame.height/10)
            let size = CGSize(width: self.frame.width/2, height: self.frame.height * 0.8)
            let rect = CGRect(origin: origin, size: size)
            let label = UILabel(frame: rect)
            let attributes: [String : AnyObject] = [NSFontAttributeName : UIFont(name: "Verdana", size: 16.0)!,
            NSForegroundColorAttributeName : UIColor.whiteColor()]
    
            label.attributedText = NSAttributedString(string: text, attributes: attributes)
    
            self.addSubview(label)
        }
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    
        }
    
        func addCircledNumber() {
            let height = self.frame.height * 0.4;
            let circleDimensions = CGSize(width: height , height:height)
            let origin = CGPointMake(self.frame.width * 0.15, self.frame.height - self.frame.height/1.5)
            let circleSubview = UIView(frame: CGRect(origin: origin, size: circleDimensions))
            circleSubview.layer.cornerRadius = height/2;
            circleSubview.backgroundColor = UIColor.darkGrayColor()
    
            let labelHeight = height * 0.8;
    
            let xPosition = circleSubview.bounds.origin.x + 3
            let yPosition = circleSubview.bounds.origin.y + 2
    
            let labelOrigin =  CGPoint(x: xPosition, y: yPosition)
            let labelRect = CGRect(origin: labelOrigin, size: CGSize(width: labelHeight, height: labelHeight))
            let numberLabel = UILabel(frame: labelRect);
            numberLabel.textAlignment = NSTextAlignment.Center
    
            let numberAsString = NSString(format: "%i", circledNumber!) as String
            let attributes: [String : AnyObject] = [NSFontAttributeName : UIFont(name: "Verdana", size: 16.0)!,
            NSForegroundColorAttributeName : UIColor.whiteColor()]
            numberLabel.attributedText = NSAttributedString(string: numberAsString, attributes: attributes)
            circleSubview.addSubview(numberLabel);
            self.circleSubview = circleSubview
            self.addSubview(circleSubview)
        }
    

    Then in your View Controller, use the initilizer I wrote:

    func addCoolButton() {
        let rect = CGRect() //choose the frame for your button here
        let button = UICoolButton(frame: rect, labelText: "Example", circledNumber: 10);
    
        let backgroundColor = UIColor(red: 243/255.0 , green: 93/255.0, blue: 118/255.0, alpha: 1.0)
        let numberColor = UIColor(red: 252/255.0, green: 118.0/255.0, blue: 135/255.0, alpha: 1.0)
        button.setColors(numberColor, backgroundColor: backgroundColor)
    
        self.view.addSubview(button)
    }