2

I need to display a fraction in my app but cannot find a good way to do it?

Should looks something like this

enter image description here(proof of concept... don't need the font):

There is other posts similar to this but they are in ObJ C, I can not find a reliable solution in swift

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Arch
  • 127
  • 2
  • 12
  • There are certain fractions contained within Unicode: https://en.wikipedia.org/wiki/Number_Forms –  Feb 05 '16 at 01:39
  • 1
    Possible duplicate of [Display fraction number in UILabel](http://stackoverflow.com/questions/30859359/display-fraction-number-in-uilabel) –  Feb 05 '16 at 01:43
  • Thats in ObjC @KennethBruno as for your first link I need many type of fractions not a set amount, – Arch Feb 05 '16 at 02:11
  • True but it's easily translatable to Swift. I'll post an answer for the Swift version. –  Feb 05 '16 at 02:13
  • 2
    The new San Francisco system fonts have this feature built in. You should have a look at [Introducing the New System Fonts](https://developer.apple.com/videos/play/wwdc2015-804/) from WWDC 2015 – Palle Feb 05 '16 at 19:47

5 Answers5

10

This is just Apple sample code from the Introducing the New System Fonts video from WWDC 2015 put into a playground and using a UILabel to render plain text fractions using font features. [Updated for Swift 4]

//: Playground - noun: a place where people can play

import UIKit
import CoreGraphics

let pointSize : CGFloat = 60.0
let systemFontDesc = UIFont.systemFont(ofSize: pointSize,
                                       weight: UIFont.Weight.light).fontDescriptor
let fractionFontDesc = systemFontDesc.addingAttributes(
    [
        UIFontDescriptor.AttributeName.featureSettings: [
            [
                UIFontDescriptor.FeatureKey.featureIdentifier: kFractionsType,
                UIFontDescriptor.FeatureKey.typeIdentifier: kDiagonalFractionsSelector,
                ],
        ]
    ] )

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 100))

label.font = UIFont(descriptor: fractionFontDesc, size:pointSize)
label.text = "12/48" // note just plain numbers and a regular slash

Just tap on the eye in the playground and you will see a beautiful fraction.

Introducing the New System Fonts (WWDC 2015 at 20:24)

Glenn Howes
  • 5,447
  • 2
  • 25
  • 29
  • 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
    Well you could use variants of the digit characters, like the set of full width mathematical digits: 0123456789. Those don't appear to have the same conversion problem. – Glenn Howes Nov 15 '16 at 15:20
  • Thanks - even better, this answer on how to use it as part of an attributed string: http://stackoverflow.com/a/34884572/428981 – RanLearns Nov 16 '16 at 18:03
5

I had to do something similar in an App. I created a mapping between common fractions and the related unicode characters like so:

enum Fraction: Double {
    case Eighth = 0.125
    case Quarter = 0.25
    case Third = 0.333333333333333
    case Half = 0.5
    case TwoThirds = 0.666666666666667
    case ThreeQuarters = 0.75
}

func localizedStringFromFraction(fraction: Fraction) -> String {
    switch fraction {
    case .Eighth:
        return NSLocalizedString("\u{215B}", comment: "Fraction - 1/8")
    case .Quarter:
        return NSLocalizedString("\u{00BC}", comment: "Fraction - 1/4")
    case .Third:
        return NSLocalizedString("\u{2153}", comment: "Fraction - 1/3")
    case .Half:
        return NSLocalizedString("\u{00BD}", comment: "Fraction - 1/2")
    case .TwoThirds:
        return NSLocalizedString("\u{2154}", comment: "Fraction - 2/3")
    case .ThreeQuarters:
        return NSLocalizedString("\u{00BE}", comment: "Fraction - 3/4")
    }
}

If you need support for more fractions, you can find the mapping here.

tebs1200
  • 1,205
  • 12
  • 19
2

@KennethBruno's answer works but I find it a little inelegant. Here's my version:

let superscriptDigits = Array("⁰¹²³⁴⁵⁶⁷⁸⁹".characters)
let subscriptDigits = Array("₀₁₂₃₄₅₆₇₈₉".characters)

func vulgarFractionWithNumerator(numerator: UInt, denominator: UInt) -> String {
    let zeroBias = UnicodeScalar("0").value
    let supers = "\(numerator)".unicodeScalars.map { superscriptDigits[Int($0.value - zeroBias)] }
    let subs = "\(denominator)".unicodeScalars.map { subscriptDigits[Int($0.value - zeroBias)] }
    return String(supers + [ "⁄" ] + subs)
}

vulgarFractionWithNumerator(123, denominator: 45678)

Result:

"¹²³⁄₄₅₆₇₈"

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • I was never big on doing math involving characters and their ASCII or Unicode values. It certainly works and is more compact but more elegant is a matter of opinion. I prefer to hard-code the translation and make it explicit. Anyways, @GlenHowes version trumps us all in elegance! –  Feb 05 '16 at 19:48
0

Here is a Swift solution for arbitrary fractions:

enum ScriptType {
  case Superscript
  case Subscript
}

func createSuperOrSubscriptDigit(character:Character, type:ScriptType) -> Character {
  switch character {
  case "0": return type == .Superscript ? "\u{2070}" : "\u{2080}"
  case "1": return type == .Superscript ? "\u{00b9}" : "\u{2081}"
  case "2": return type == .Superscript ? "\u{00b2}" : "\u{2082}"
  case "3": return type == .Superscript ? "\u{00b3}" : "\u{2083}"
  case "4": return type == .Superscript ? "\u{2074}" : "\u{2084}"
  case "5": return type == .Superscript ? "\u{2075}" : "\u{2085}"
  case "6": return type == .Superscript ? "\u{2076}" : "\u{2086}"
  case "7": return type == .Superscript ? "\u{2077}" : "\u{2087}"
  case "8": return type == .Superscript ? "\u{2078}" : "\u{2088}"
  case "9": return type == .Superscript ? "\u{2079}" : "\u{2089}"
  default: return character
  }
}

func createSuperOrSubscriptDigits(string:String, type:ScriptType) -> String {
  return String(string.characters.map() { createSuperOrSubscriptDigit($0, type: type) })
}

extension String {
  func createSuperscriptDigits() -> String {
    return createSuperOrSubscriptDigits(self, type: .Superscript)
  }
  func createSubscriptDigits() -> String {
    return createSuperOrSubscriptDigits(self, type: .Subscript)
  }
}

func fractionString(numerator:String, denominator:String) -> String {
  return numerator.createSuperscriptDigits() + "\u{2044}" + denominator.createSubscriptDigits()
}
  • Your should check out the 'Introducing the New System Fonts" video from WWDC 2015 at around the 20:24 mark which goes into making these pretty via Font Features. https://developer.apple.com/videos/play/wwdc2015-804/ – Glenn Howes Feb 05 '16 at 19:35
  • @GlennHowes That's a nice feature, certainly a great way to go with it. –  Feb 05 '16 at 19:43
0

I had the same issue and wrote FractionFormatter, and extension of NumberFormatter, to solve this issue.

let fractionFormatter = FractionFormatter()
fractionFormatter.string(from: 1.5) // "1 ½"
David W. Keith
  • 2,246
  • 17
  • 20