skip to Main Content

I have a label. If the text count in that label is 30, then after the 20th character there should be a line break. How can we achieve that?

I have the label setup as below.

        let label = MyLabelText()
        label.numberOfLines = 2
        label.lineBreakMode = .byTruncatingTail
        label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        
    

Both line break and number of lines are defined. But I’m not sure how to go to the second line after the 20th character.

2

Answers


  1. You can use a String extension:

    extension String {
    
        mutating func insert(sourceString: String, string: String, indx: Int) {
    
            do {
    
                if indx > sourceString.count {
                  
                } else {
    
                    try insert(contentsOf: string, at: index(startIndex, offsetBy: indx))
                }
    
            // This do-catch is to make sure nothing happens if is an issue
    
            } catch Your.Execption {
              
            }
        }
    }
    

    And then use it like this:

    myString.insert(sourceString: myString, string: "n", indx: 20)

    Login or Signup to reply.
  2. A clean way to achieve this is to extend String so it can provide a ‘wrapped’ version of itself, and then use this in a subclass of UILabel to keep things clean at the point of use.

    So extend String to wrap itself into a multi-line string at a certain character width:

    extension String {
        func wrap(at width: Int) -> String {
            return self
                .indices
                .enumerated()
                .reduce(""){
                    let charAsString = String(self[$1.1])
                    let position = $1.0
                    guard position != 0 else {return charAsString}
                    if position.isMultiple(of: width) {
                        return $0 + "n" + charAsString
                    } else {
                        return $0 + charAsString
                    }
                }
        }
    }
    

    A couple of things of note:

    • you need to use the original indices, not the just the length of the produced string or it’s indices as adding the line break affects the number of characters
    • you actually want to wrap the original string, i.e. insert the line break, every width + 1 characters. Helpfully sequence enumerations are 0-indexed so you get the +1 for free 🙂
    • you could put the whole reduce closure into a single line ternary operation. I did initially and it was hideous to read, so an if...else is far more maintainable.

    Once this is in place, creating the custom UILAbel is pretty straightforward. Create a subclass and override the text property:

    class WrappedLabel: UILabel {
        let width: Int
        init(withWidth width: Int, frame: CGRect = .zero) {
            self.width = width
            super.init(frame: frame)
            numberOfLines = 0  //to allow auto-sizing of the label
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override var text: String? {
            set {
                guard let newValue = newValue else {return}
                super.text = newValue.wrap(at: width)
            }
            get {
                super.text
            }
        }
    }
    

    Implementation is then as simple as

    let label = WrappedLabel(withWidth: 20)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search