iosipadswift3xcode8ppi

show a exactly Height of text in a TextField


I have to program a see test for an optician. So they need that the Numbers are exactly have a specific height. He has a IPad ans streams it to a TV. I know i must consider the PPI of the TV. Here is my Code :

func calcPoints() -> Float {
    // he gives the Visus with a TextField
    let visus = Float(textFieldVisus.text!)
    // Here you put in the PPI of the device
    let ppi = Float(textFieldDPI.text!)
    // Calculate the lenght of the Number
    let lenght = ((0.29 * 5) / visus!) * 5
    // Calculate the Points (because TextFiels work with Points)
    let points = ((ppi! / 25.4) * lenght) * 0.75
    // here you divide with 2 because you have a retina Display on the IPad
    return (points / 2)
}

func passeUIan() {
    // Now i can give the Points to the TextField
    textField1.bounds.size.width = CGFloat(calcPoints())
    textField1.bounds.size.height = CGFloat(calcPoints())
    textField1.font = UIFont(name: (textField1.font?.fontName)!, size: CGFloat(calcPoints()))
}

but when i measure off the lenght on the TV it is wrong. Normaly it must be 7.25mm but it is approximately 9mm. i dont know what is wrong. I search for this problem since 2 weeks...


Solution

  • You need to first familiarize yourself with the different font metrics. The font size is usually (but not always) the difference between the ascender and descenders. For your purpose, the height of an uppercase letter is called the "cap height" and the height of a lowercase letter is called the "x-height".

    There's no formula to translate the font size to either cap height or x-height. Their relationships different from fonts to fonts, and even variants (bold, italic, small caps, display, book) within a font.

    The function below use binary search to look for a point size that matches your desired height (in inches):

    // desiredHeight is in inches
    func pointSize(inFontName fontName: String, forDesiredCapHeight desiredHeight: CGFloat, ppi: CGFloat) -> CGFloat {
        var minPointSize: CGFloat = 0
        var maxPointSize: CGFloat = 5000
        var pointSize = (minPointSize + maxPointSize) / 2
    
        // Finding for exact match may not be possible. UIFont may round off
        // the sizes. If it's within 0.01 in (0.26 mm) of the desired height,
        // we consider that good enough
        let tolerance: CGFloat = 0.01
    
        while let font = UIFont(name: fontName, size: pointSize) {
            let actualHeight = font.capHeight / ppi * UIScreen.main.scale
    
            if abs(actualHeight - desiredHeight) < tolerance {
                return pointSize
            } else if actualHeight < desiredHeight {
                minPointSize = pointSize
            } else {
                maxPointSize = pointSize
            }
    
            pointSize = (minPointSize + maxPointSize) / 2
        }
    
        return 0
    }
    

    Example: find the point size that make the capital letter 1-inch tall in Helvetica. (326 is the PPI for iPhone 6 / 6S / 7, which I used to test):

    let size = pointSize(inFontName: "Helvetica", forDesiredCapHeight: 1, ppi: 326)
    label.font = UIFont(name: fontName, size: size)
    label.text = "F"
    

    (tips: a UILabel handles font size much better than a UITextField)