TextField 的 UTF-8 字符限制无法正常工作

问题描述 投票:0回答:1

我的应用程序中有一个文本字段需要限制其字符数。它适用于常规文本。但我需要考虑特殊字符和表情符号,当文本字段达到限制时,我删除一个字符,然后重复输入例如 ¢ (2 字节)或 € (3 字节),它的行为就好像没有字符一样完全没有限制,您可以输入任何内容。

TextField("Username", text: $ame)
    .onChange(of: name, perform: { value in
        totalBytes = value.utf8.count
        // Only mess with the value if it is too big
        if totalBytes > 14 {
            let firstNBytes = Data(name.utf8.prefix(14))
            if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
                // Set the message back to the last place where it was the right size
                name = maxBytesString
            } else {
                print("not a valid UTF-8 sequence")
            }
        }
    })

控制台确实会打印“无效的 UTF-8”警告,但在这种边缘情况下,文本字段永远不会得到纠正。有什么帮助吗?顺便说一句,“name”是一个 @State 变量,如果重要的话,可以使用

onAppear
方法成功初始化。

swift swiftui utf-8 character-encoding textfield
1个回答
0
投票

问题在于

let firstNBytes = Data(name.utf8.prefix(14))
。正如您所看到的,某些字符超过 1 个字节。当您仅获取第一个 14 个字节时,您可以轻松地将一个字符切成两半。这就是为什么尝试从截断的字节创建
String
可能会失败。

您需要一种方法来逐字节修剪数据,从 14 开始,直到获得有效数据。您最终得到的字符串可能只有 13、12、11 甚至更少字节,但至少有一个小于 14 字节的字符串。

这是

String
的扩展,它将根据提供的字节限制返回截断的字符串:

extension String {
    func truncated(to bytes: Int, encoding: String.Encoding = .utf8) -> String? {
        if let data = self.data(using: encoding) {
            var tmpData = data.prefix(bytes)
            repeat {
                if let res = String(data: tmpData, encoding: encoding) {
                    return res
                } else {
                    tmpData = tmpData.dropLast(1)
                }
            } while !tmpData.isEmpty
        }

        return nil // Should never be reached with UTF-8 encoding
    }
}

这样你的代码就会变成如下所示:

TextField("Username", text: $name)
    .onChange(of: name, perform: { value in
        totalBytes = value.utf8.count
        // Only mess with the value if it is too big
        if totalBytes > 14 {
            if let newName = value.truncated(to: 14) {
                name = newName
            }
        }
    })
© www.soinside.com 2019 - 2024. All rights reserved.