BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

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      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.