今日说说在开发中很常用的两个特性,它们也同样是两个特点包装器。
@FocusState 用于办理视图元素的输入焦点状态。
@AppStorage 用于将特点存储在 UserDefaults 中

接下来咱们用一个例子来说明


struct FocusStateSample: View {
    let textFieldBackgroundColor = #colorLiteral(red: 0.9496834874, green: 0.9635508657, blue: 1, alpha: 1)
    @State private var name: String = ""
    @State private var password: String = ""
    @State private var againPassword: String = ""
    @State private var email: String = ""
    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 20) {
                    markTextField("Input your name", bindingName: $name, submitLabel: .next, keyboardTypeL: .default)
                    markTextField("Input your password", bindingName: $password, submitLabel: .next, keyboardTypeL: .default)
                    markTextField("Input your password again", bindingName: $againPassword, submitLabel: .next, keyboardTypeL: .default)
                    markTextField("Input your email", bindingName: $email, submitLabel: .done, keyboardTypeL: .emailAddress)
                    Button {
                    } label: {
                        Text("Save".uppercased())
                            .foregroundColor(.white)
                            .font(.headline)
                            .fontWeight(.semibold)
                            .frame(height: 55)
                            .frame(maxWidth: .infinity)
                            .background(Color.blue.cornerRadius(10))
                    }
                    Spacer()
                }
            }
            .padding()
            .navigationTitle("Focus state")
            .onTapGesture {
                dismissKeyboard()
            }
        }
    }
    // Hide keyboard, When you tap blank space
    func dismissKeyboard(){
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
    private func markTextField(
        _ prompt: String,
        bindingName: Binding<String>,
        submitLabel: SubmitLabel,
        keyboardTypeL: UIKeyboardType
    ) -> some View {
        TextField(prompt, text: bindingName)
            .font(.headline)
            .frame(height: 55)
            .submitLabel(submitLabel)
            .keyboardType(keyboardTypeL)
            .padding(.horizontal)
            .background(Color(uiColor: textFieldBackgroundColor))
            .cornerRadius(10)
    }
}

@FocusState and @AppStorage inSwiftUI

现有代码是构建了四个输入框和一个Button,而且每个输入框的提交键盘按钮和运用的键盘类型都有所不同。

SubmitLabel

指定提交按钮的文字,SubmitLabel 是一个枚举,能够指定以下枚举中的任何值

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct SubmitLabel {
    /// Defines a submit label with text of "Done".
    public static var done: SubmitLabel { get }
    /// Defines a submit label with text of "Go".
    public static var go: SubmitLabel { get }
    /// Defines a submit label with text of "Send".
    public static var send: SubmitLabel { get }
    /// Defines a submit label with text of "Join".
    public static var join: SubmitLabel { get }
    /// Defines a submit label with text of "Route".
    public static var route: SubmitLabel { get }
    /// Defines a submit label with text of "Search".
    public static var search: SubmitLabel { get }
    /// Defines a submit label with text of "Return".
    public static var `return`: SubmitLabel { get }
    /// Defines a submit label with text of "Next".
    public static var next: SubmitLabel { get }
    /// Defines a submit label with text of "Continue".
    public static var `continue`: SubmitLabel { get }
}

办法定义如下:

public func submitLabel(_ submitLabel: SubmitLabel) -> some View

@FocusState and @AppStorage inSwiftUI

以上button的提交按钮分别设置了 .next.done

咱们现在有以下需求,会在上述代码基础上来完结。

需求:

  1. 需求在 App显现页面时,主动把焦点放在第一个输入用户名的TextField上
  2. 当点击键盘的Next按钮时,主动把焦点放在下一个TextField中。比如:我现在的焦点在输入用户名的TextField中,当我点击Next时就主动焦点到passwordTextField上
  3. 点击Save保存一切输入框的数据,下次发动时显现前次保存的值

咱们首先来完结第一个需求

需求在 App显现页面时,主动把焦点放在第一个输入用户名的TextField上

此刻咱们需求声明一个运用FocusState润饰的特点,它的类型FocusState ,详细如下:

  1. 声明FocusState润饰的特点,Boolean类型的,无初始值

@FocusState var nameFocused: Bool
  1. 给运用的TextField增加上下面的特点
.focused($nameFocused)
  1. 在OnAppear办法里边调用(0.5 秒后调用)
.onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
                    nameFocused.toggle()
                })
            }

即可实现需求一

@FocusState and @AppStorage inSwiftUI

接下来完结需求二

当点击键盘的Next按钮时,主动把焦点放在下一个TextField中。比如:我现在的焦点在输入用户名的TextField中,当我点击Next时就主动焦点到passwordTextField上

其实需求二是在需求一的基础上做了加强,咱们需求给咱们一切的TexTField目标加上@FocusState特点包装器

@FocusState var nameFocused: Bool
@FocusState var passwordFocused: Bool
@FocusState var againPasswordFocused: Bool
@FocusState var emailFocused: Bool

一起需求在TextField弥补一个提交办法onSubmit

.onSubmit {
                if nameFocused {
                    nameFocused = false
                    passwordFocused = true
                } else if passwordFocused {
                    passwordFocused = false
                    againPasswordFocused = true
                } else if againPasswordFocused {
                    againPasswordFocused = false
                    emailFocused = true
                } else {
                    print("Done")
                }
            }

经过需求一,咱们理解。经过切换被@FocusState润饰的特点能够来实现当时TextFiedl是否有焦点,也便是是否能够运用键盘。那么上面办法便是判别当时TextField是否能够主动聚焦。其实你能够运用TextField里边的内容来判别,如果满意你当时的条件,你再切换到下一个TextField输入,例如:name.count > 8 等

@FocusState and @AppStorage inSwiftUI

点击Save保存一切输入框的数据,下次发动时显现前次保存的值

那么咱们改如何在本次保存数据后,下次发动时主动显现数据呢?(当咱们点击Save Button 时咱们不再判别输入的内容。默许输入框都是有内容的)

接下来咱们会介绍AppStorage,它的底层是UserDefault. 咱们现在需求声明s四个特点来保存四个输入框的值

@AppStorage var currentName: String?
@AppStorage var currentPassword: String?
@AppStorage var currentAgainPassword: String?
@AppStorage var currentEmail: String?

然后需求构建一个Save办法

private func save() {
        currentName = name
        currentPassword = password
        currentAgainPassword = againPassword
        currentEmail = email
        print("Saved")
    }

而且在 Button 办法中调用save办法

Button {
  save()
}

保存的时其实就做完了,可是还差一个在下次发动主动填充值的需求。咱们来完结以下

先把数据读出来

?? 的意思是:当?? 前面的值为空时,便是用后面的值

private func autoFillContent() {
        name = currentName ?? ""
        password = currentPassword ?? ""
        againPassword = currentAgainPassword ?? ""
        email = currentEmail ?? ""
    }

挂载到onAppear办法上

.onAppear {
                autoFillContent()
                guard name.count == 0 else { return }
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
                    nameFocused = true
                })
            }

上述需求已经都完结了,可是代码是存在问题的。在生产环境这样写代码是不行的。我说出以下几点问题:

  1. save之前需求校验输入的内容
  2. 主代码视图内需求精简,不要过于太长,避免看的脑壳疼。要做好高内聚
  3. 暗码框应该是秘文方式
  4. 考虑:如果我的输入框不止这四个,如果是一个简历输入页面,将有很多信息要录入,那么如果一个页面的TextField有10个该怎样办?还是运用上述办法吗?

下一节咱们将说说怎样去适配多个TextField的情况,加油!

关于今日的内容,大家有什么看法呢?欢迎留言评论。
公众号:RobotPBQ