1

i was wondering if there was a builtin method to represent

var someFraction = "1/12"

as an attributed string? i.e. the "1" is raised and compressed, whilst the "12" is lowered and also compressed.

thanks

ajrlewis
  • 2,968
  • 3
  • 33
  • 67

3 Answers3

6

If you want to have arbitrary fractions represented correctly, you should set the UIFontFeatureTypeIdentifierKey and UIFontFeatureSelectorIdentifierKey to kFractionsType and kDiagonalFractionsSelector respectively for UIFontDescriptorFeatureSettingsAttribute in a custom UIFontDescriptor. For example you can say something like:

let label = UILabel(frame: CGRect(x: 0.0, y: 0.0, width: 1000.0, height: 100.0))
let pointSize : CGFloat = 60.0
let systemFontDesc = UIFont.systemFontOfSize(pointSize,
    weight: UIFontWeightLight).fontDescriptor()
let fractionFontDesc = systemFontDesc.fontDescriptorByAddingAttributes(
    [
        UIFontDescriptorFeatureSettingsAttribute: [
            [
                UIFontFeatureTypeIdentifierKey: kFractionsType,
                UIFontFeatureSelectorIdentifierKey: kDiagonalFractionsSelector,
            ], ]
    ] )
label.font = UIFont(descriptor: fractionFontDesc, size:pointSize)
label.text = "The Fraction is: 23/271"

with the following result:

enter image description here

You can find more information here

Swift 3.0

extension UIFont
{
    static func fractionFont(ofSize pointSize: CGFloat) -> UIFont
    {
        let systemFontDesc = UIFont.systemFont(ofSize: pointSize).fontDescriptor
        let fractionFontDesc = systemFontDesc.addingAttributes(
            [
                UIFontDescriptorFeatureSettingsAttribute: [
                    [
                        UIFontFeatureTypeIdentifierKey: kFractionsType,
                        UIFontFeatureSelectorIdentifierKey: kDiagonalFractionsSelector,
                    ], ]
            ] )
        return UIFont(descriptor: fractionFontDesc, size:pointSize)
    }
}

let label = UILabel()
label.backgroundColor = .white
let pointSize: CGFloat = 45.0
let string = "This is a mixed fraction 312/13"
let attribString = NSMutableAttributedString(string: string, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: pointSize), NSForegroundColorAttributeName: UIColor.black])
attribString.addAttributes([NSFontAttributeName: UIFont.fractionFont(ofSize: pointSize)], range: (string as NSString).range(of: "12/13"))
label.attributedText = attribString
label.sizeToFit()

enter image description here

Community
  • 1
  • 1
beyowulf
  • 15,101
  • 2
  • 34
  • 40
  • Thanks for this! Any idea if there is a way to use this font feature to create a fraction with a number in front of it like 3 ⅚? I thought that it would only superscript and subscript numbers on either side of a slash, but any numbers used in a font with this feature become superscript. Maybe there is a way to break for certain characters? – RanLearns Nov 15 '16 at 02:27
  • 1
    I don't think that is possible. One way you can do it is by using an attributed string. See my updated answer for an example. – beyowulf Nov 15 '16 at 16:13
  • @RanLearns see my answer for a generic approach to "3 5/6" – ajrlewis Aug 09 '18 at 21:22
1

You could use unicode to display fractions instead of using Attributed strings. This link has a source code written in objective C . You can easily port it in swift.

Community
  • 1
  • 1
Vignesh
  • 10,205
  • 2
  • 35
  • 73
0

Using the above UIFont extension:

static func fractionFont(forTextStyle textStyle: UIFontTextStyle) -> UIFont {

    let font = UIFont.preferredFont(forTextStyle: .title1)
    let descriptor = font.fontDescriptor.addingAttributes(
        [
            UIFontDescriptor.AttributeName.featureSettings: [
                [
                    UIFontDescriptor.FeatureKey.featureIdentifier: kFractionsType,
                    UIFontDescriptor.FeatureKey.typeIdentifier: kDiagonalFractionsSelector,
                    ],
            ]
        ] )

    return UIFont(descriptor: descriptor, size: font.pointSize)

}

Then the following function somewhere:

func fractionMutableAttributedString(for string: String) -> NSMutableAttributedString {

    let attributes: [NSAttributedStringKey: Any] = [.foregroundColor: UIColor.blue]
    let attributedText = NSMutableAttributedString(string: string, attributes: attributes)

    let substring = string.split(separator: " ") // Do we have a fractional value?
    if substring[1].contains("/") {
         let range = (string as NSString).range(of: String(substring[1]))
        attributedText.addAttribute(NSAttributedStringKey.font, value: UIFont.fractionFont(forTextStyle: .title1), range: range)
     }

     return attributedText

}

and the use as:

let string = "3 5/6"
label.attributedText = fractionMutableAttributedString(for: string)

to produce the correct fraction.

ajrlewis
  • 2,968
  • 3
  • 33
  • 67