首页 > 解决方案 > SwiftUI 更改 SecureField 掩码字符

问题描述

MacOS 11 上的 SwiftUI

目标是让 SwiftUI SecureField 显示与默认项目符号 (••••••) 不同的 Unicode 字符,例如表情符号、随机生成的字符等。目标的一个重要部分是实际的文本输入by user 是完全可编辑和保留的,并且可以在@State变量中访问,并且仅显示掩码字符,但我不介意它是通过 TextField 或其他一些 View 实现的。

例如,香草 SecureField 项目符号:

struct ContentView : View {

  @State var password : String = ""

  var body: some View {

    VStack {
      SecureField("Password", text: $password)
      Button("Transmogrify!") {}
    }.padding()

  }
}

结果是:

标准 SecureField 输出

目标是实现与 SecureField 相同的行为,但显示不同的字符,如下所示:

修改 SecureField 输出

到目前为止,我还没有想出一个有效的代码示例。我曾尝试将纯文本TextField与显式结合使用Binding<String>来尝试控制底层文本get/set但由于绑定的性质会影响最终存储在password

标签: swiftmacosswiftui

解决方案


你可以用代理来做

import SwiftUI
//Shows a sample use
@available(iOS 15.0, macOS 12.0, *)
struct SecureParentView: View{
    @State var text: String = "secure"
    var body: some View{
        VStack{
            Text(text)
            MySecureFieldView(text: $text)
        }
    }
}
//The custom field
@available(iOS 15.0, macOS 12.0, *)
struct MySecureFieldView: View {
    @Binding var text: String
    //The proxy handles the masking
    var proxy: Binding<String>{
        Binding(get: {
            return text.map { _ in "\u{272A}" }.joined() 
        }, set: { value in
            //Not needed here because the TextField is focused
        })
    }
    @FocusState private var focusedField: Int?
    var body: some View {
        //This is for size. The 3 layers have to match so the cursor doesn't look off
        Text(text).lineLimit(1)
            .opacity(0)
        //Put a true secure field on top that has invisible text/dots
            .overlay(
                SecureField("actual", text: $text)
                    .foregroundColor(Color.clear)
                    .focused($focusedField, equals: 1)
                    .minimumScaleFactor(0.2)
                
            )
            .background(
                //This will sit on below but will look like it is on top
                //It will reduce in size to match lettering
                Text(proxy.wrappedValue)
                    .lineLimit(1)
                    .minimumScaleFactor(0.2)
                    .foregroundColor(Color(UIColor.label))
                    .opacity(1)
            )
            .foregroundColor(Color(UIColor.clear))
            .onTapGesture {
                focusedField = 1
            }
    }
    
}

@available(iOS 15.0, macOS 12.0, *)
struct SecureParentView_Previews: PreviewProvider {
    static var previews: some View {
        SecureParentView()
    }
}

在此处输入图像描述


推荐阅读