Search code examples
swiftuilabelfractions

Display a fraction in one UILabel


I am trying to display a fraction as something like 1/2 but I want to line to be horizontal so the fraction looks more fraction like, some thing like this:

1
_
2

However with no massive space and in a way that I can use it inside one UILabel (which can have multiple lines). I also need to be able to put a fraction as the numerator/denominator of a fraction and so on. Any suggestions? (Fonts, characters...)

Thank you, Gleb Koval


Solution

  • Here's a UIView subclass that draws a fraction:

    class FractionView: UIView {
        var font: UIFont = UIFont.systemFont(ofSize: 16) {
            didSet { self.setNeedsDisplay() }
        }
        var numerator: Int = 1 {
            didSet { self.setNeedsDisplay() }
        }
        var denominator: Int = 2{
            didSet { self.setNeedsDisplay() }
        }
        var spacing: CGFloat = 5{
            didSet { self.setNeedsDisplay() }
        }
    
        override func draw(_ rect: CGRect) {
            let numString = "\(numerator)" as NSString
            let numWidth = numString.size(withAttributes: [.font: font]).width
            let denomString = "\(denominator)" as NSString
            let denomWidth = denomString.size(withAttributes: [.font: font]).width
            let numX: CGFloat
            let denomX: CGFloat
    
            if numWidth <= denomWidth {
                denomX = 0
                numX = (denomWidth - numWidth) / 2
            } else {
                denomX = (numWidth - denomWidth) / 2
                numX = 0
            }
    
            numString.draw(at: CGPoint(x: numX, y: 0), withAttributes: [.font : font])
            let path = UIBezierPath()
            path.move(to: CGPoint(x: 0, y: font.lineHeight + spacing))
            path.addLine(to: CGPoint(x: self.frame.maxX, y: font.lineHeight + spacing))
            UIColor.black.setStroke()
            path.lineWidth = 1
            path.stroke()
            denomString.draw(at: CGPoint(x: denomX, y: font.lineHeight + spacing * 2), withAttributes: [.font: font])
        }
    }
    
    // usage:
    let width = ("12" as NSString).size(withAttributes: [.font: UIFont.systemFont(ofSize: 16)]).width
    let view = FractionView(frame: CGRect(x: 0, y: 0, width: width, height: 48))
    view.backgroundColor = .white
    view.denominator = 12
    

    enter image description here

    As Sulthan in comments has suggested, using an NSAttributedString would be an easier approach:

    let attrString = NSMutableAttributedString(string: "1", attributes: [NSAttributedStringKey.underlineStyle : 1])
    attrString.append(NSAttributedString(string: "\n2"))
    attrString
    let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    label.attributedText = attrString
    label.numberOfLines = 2
    

    enter image description here