13

When using NSKernAttributeName it puts a space at the end of each line, is there any way to fix this? I can set the attributed to be in the range of:

NSRange(location: 0, length: self.text!.characters.count-1)

But I don't want to set this for every line.

This is the test code in the a playground I am using

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

import UIKit
import XCPlayground

var text = "Hello, playground\nhow are you?"

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 50
paragraphStyle.alignment = NSTextAlignment.Left
paragraphStyle.lineBreakMode = NSLineBreakMode.ByTruncatingTail

let attributes = [
    NSParagraphStyleAttributeName: paragraphStyle
    , NSKernAttributeName: 20
]


let attributedString = NSAttributedString(string: text, attributes: attributes)

let label = UILabel()
label.attributedText = attributedString
label.numberOfLines = 0
label.textColor = UIColor.greenColor()
label.backgroundColor = UIColor.orangeColor()
label.sizeToFit()
label.center = CGPoint(x: 500, y: 100)


var text2 = "What's up\nWhere are you?"
let attributedString2 = NSAttributedString(string: text2, attributes: attributes)

let label2 = UILabel()
label2.attributedText = attributedString2
label2.numberOfLines = 0
label2.textColor = UIColor.greenColor()
label2.backgroundColor = UIColor.orangeColor()
label2.sizeToFit()
label2.center = CGPoint(x: 500, y: 250)

var text3 = "Hello"
let attributedString3 = NSAttributedString(string: text3, attributes: attributes)

let label3 = UILabel()
label3.attributedText = attributedString3
label3.numberOfLines = 0
label3.textColor = UIColor.greenColor()
label3.backgroundColor = UIColor.orangeColor()
label3.sizeToFit()
label3.center = CGPoint(x: 500, y: 400)

let holderView = UIView(frame: CGRect(x: 0, y: 0, width: 1000, height: 500))
holderView.backgroundColor = UIColor.lightGrayColor()
holderView.addSubview(label)
holderView.addSubview(label2)
holderView.addSubview(label3)

XCPlaygroundPage.currentPage.liveView = holderView

With the result looking like this:

text with kerning at the end of a line

You can see the spaces at the end of each of the lines.

Sunil Sharma
  • 2,653
  • 1
  • 25
  • 36
richy
  • 2,716
  • 1
  • 33
  • 42

2 Answers2

2

This is actually the definition of how kerning works; it adjusts the space between the kerned character and where the next character will be. Whether a next character proceeds to be drawn or not is irrelevant.

Standard Attributes

The kerning attribute indicates how much the following character should be shifted from its default offset as defined by the current character’s font; a positive kern indicates a shift farther along and a negative kern indicates a shift closer to the current character.

If it helps, think about doing this in a word processor. If kerning is on, and you type a character, where would you expect the insertion point to be then? The expected answer would be "offset from the just typed character by the amount of kern" as that's what happens in the default case of kern being 0, correct? Well, that's exactly what's happening here: if you kern the last character of a string, the string therefore includes the last kern.

So the correct thing to do here is to wrap up your dropLast() logic in an extension and call it a day.

Alex Curylo
  • 4,744
  • 1
  • 27
  • 37
  • It doesn't help much. If you think how *decent* word processor works: it will first try to fit characters on the line ignoring the kerning/tracking on the last character, and then once it knows the characters it will perform alignments - in case it has to justify the line. So you're right that kerning does exactly what Apple describes it to do, but it doesn't give control of what decent word processor would allow you to do. – Paulius Liekis Jul 20 '23 at 20:49
  • Even if you make some sort of `dropLast()` extension it doesn't really solve the problem once you have automatically wrapped lines. – Paulius Liekis Jul 20 '23 at 20:50
-1

Create an extension

import UIKit
extension UILabel {

    @IBInspectable var kerning: Float {
        get {
            var range = NSMakeRange(0, (text ?? "").characters.count)
            guard let kern = attributedText?.attribute(NSKernAttributeName, atIndex: 0, effectiveRange: &range),
            value = kern as? NSNumber
            else {
                return 0
            }
            return value.floatValue
        }
        set {
            var attText:NSMutableAttributedString?

            if let attributedText = attributedText {
                attText = NSMutableAttributedString(attributedString: attributedText)
            } else if let text = text {
                attText = NSMutableAttributedString(string: text)
            } else {
                attText = NSMutableAttributedString(string: "")
        }

            let range = NSMakeRange(0, attText!.length)
            attText!.addAttribute(NSKernAttributeName, value: NSNumber(float: newValue), range: range)
            self.attributedText = attText
        }
    }
}

This was answered here

Community
  • 1
  • 1
Mtoklitz113
  • 3,828
  • 3
  • 21
  • 40