GO FURTHER, FASTER: Try the Swift Career Accelerator today! >>

SOLVED: How do I restrict TextView input to a certain number of characters

Forums > 100 Days of SwiftUI

I have a TextView wrapped in a View (which I wish to use a Component in attempt to creating a pin code entry style widget). I was wondering if there's a way of restricting the input of a TextView to a certain number of characters?

Any pointers would be much appreciated. Current setuip is as follows (please note that the code is very experimental):

import SwiftUI

struct CharacterInputCell: View {

    @State private var textValue: String = ""

    var body: some View {
        TextField("", text: $textValue, onEditingChanged: onEditingChanged(_:), onCommit: onCommit)
        .frame(maxWidth: .infinity, alignment: .center)
        .padding([.trailing, .leading], 10)
        .padding([.vertical], 15)
        .lineLimit(1)
        .multilineTextAlignment(.center)
        .overlay(
            RoundedRectangle(cornerRadius: 6)
                .stroke(Color.red.opacity(0.5), lineWidth: 2)
        )
    }

    func onCommit() {
        print("commit")
    }

    func onEditingChanged(_ changed: Bool) {
        print(changed)
    }
}

struct CharacterInputCell_Previews: PreviewProvider {
    static var previews: some View {
        CharacterInputCell()
    }
}

3      

Hello!

I've found a workaround of limitting an input character count for TextField here on StackOverflow, and tried it in code my self and it works. See updated code.

 class TextBindingManager: ObservableObject {
    @Published var text = "" {
        didSet {
            if text.count > characterLimit && oldValue.count <= characterLimit {
                text = oldValue
            }
        }
    }
    let characterLimit: Int

    init(limit: Int = 1){
        characterLimit = limit
    }
}

struct CharacterInputCell: View {
    @ObservedObject var textBindingManager = TextBindingManager(limit: 1)

    var body: some View {
        TextField("", text: $textBindingManager.text, onEditingChanged: onEditingChanged(_:), onCommit: onCommit)
        .frame(maxWidth: .infinity, alignment: .center)
        .padding([.trailing, .leading], 10)
        .padding([.vertical], 15)
        .lineLimit(1)
        .multilineTextAlignment(.center)
        .overlay(
            RoundedRectangle(cornerRadius: 6)
                .stroke(Color.red.opacity(0.5), lineWidth: 2)
        )
    }

    func onCommit() {
        print("commit")
    }

    func onEditingChanged(_ changed: Bool) {
        print(changed)
    }
}

5      

Not sure if this is 100% efficient but I have been attempting to implement a solution using Combine and it looks something like this:

import SwiftUI
import Combine

struct PassCodeInputCell: View {

    @Binding var value: String

    var body: some View {
        TextField("", text: self.$value)
        .frame(height: 20)
        .frame(maxWidth: .infinity, alignment: .center)
        .padding([.trailing, .leading], 10)
        .padding([.vertical], 15)
        .overlay(
            RoundedRectangle(cornerRadius: 6)
                .stroke(Color.red.opacity(0.5), lineWidth: 2)
        )
        .onReceive(Just(self.value)) { inputValue in
            // With a little help from https://bit.ly/2W1Ljzp
            if inputValue.count > 1 {
                self.value.removeLast()
            }
        }
    }
}

struct PassCodeInputCell_Previews: PreviewProvider {
    static var previews: some View {
        PassCodeInputCell(value: .constant("T"))
    }
}

thoughts, feedback and specially imporvements are very welcome.

6      

Thanks @devraj for your code, it works perfectly comparing to the above one which didn't work with me really don't know why cause it looks logic. Yours is less code and doesn't create a new specific object for each single view, i really like it.

Thanks again.

3      

Thanks @devraj. I also had problems with the Stackoverflow approaches. The Combine approach looks nice. It is reusable as well if you create a custom modifier passing in a text binding and a limit.

Like this at the call site:

@State private var name = ""

...

TextField("", text: $name)
    .limitText($name, length: 30)

4      

Hacking with Swift is sponsored by Essential Developer.

SPONSORED Transform your career with the iOS Lead Essentials. This Black Friday, unlock over 40 hours of expert training, mentorship, and community support to secure your place among the best devs. Click for early access to this limited offer and a free crash course.

Save your spot

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.