Four implementation schemes of IOS swiftui data form from rookie to expert

Time:2021-3-29

Collecting data, processing data and displaying data are the most basic functions of a program. Today, let’s learn how to use swiftui’s form to collect data.

In this issue, we will take the most basic user registration interface as an example to introduce four different levels of data collection.

Beginner‘s way

I believe friends who have been reading my column for a long time can quickly build up the following interface. It’s very simple, just a simple combination of vstack, hstack, textfield and other components.

Four implementation schemes of IOS swiftui data form from rookie to expert

import SwiftUI

struct SignUpForm0: View {
    @State var username = ""
    @State var email = ""
    
    var body: some View {
        NavigationView{
            Form {
                Text ("user registration interface for beginners"). Font (. Headline)
                HStack {
                    Image(systemName: "person.circle.fill")
                    TextField("Username", text: $username)
                }
                HStack {
                    Image(systemName: "envelope.circle.fill")
                    TextField("Email", text: $email)
                }
                Button(
                    action: { print("here") },
                    Label: {text ("submit")}
                )
            }. navigationbartitle (text ("user registration interface"))
        }
    }
}

Entry level approach

The entry-level interface is exactly the same as the beginner’s, but the modular idea is used when building the interface.
In the beginner stage, we used the same combination of hstack + image + textfield twice, but this is not necessarily a problem, because our configuration of the two text fields is very different. But suppose we want to reuse the code elsewhere, how do we do it. In fact, it’s very simple. We can encapsulate the code into a separate component.

In order to realize this idea, we first create a struct view, then encapsulate image and text, and finally transfer the value through @ binding.

struct IconPrefixedTextField: View {
    var iconName: String
    var title: String
    @Binding var text: String
    
    var body: some View {
        HStack {
            Image(systemName: iconName)
            TextField(title, text: $text)
        }
    }
}

In the main interface, the newly created components are used for data collection. How to make the code more concise.

import SwiftUI

struct SignUpForm1: View {
    @State var username = ""
    @State var email = ""
    
    var body: some View {
        NavigationView{
            Form {
                Text ("user registration interface after getting started"). Font (. Headline)
                IconPrefixedTextField(
                    iconName: "person.circle.fill",
                    title: "Username",
                    text: $username
                )
                IconPrefixedTextField(
                    iconName: "envelope.circle.fill",
                    title: "Email",
                    text: $email
                )
                Button(
                    action: { print("here") },
                    Label: {text ("submit")}
                )
            }. navigationbartitle (text ("user registration interface"))
        }
    }
}

intermediate level

Just now, we have seen beginners quickly build interfaces, and the entry-level uses modular programming ideas. How can intermediate players achieve this.
Intermediate players will tell us that neither of the above two implementation methods is the best implementation mode of swiftui. Although the above changes will enable us to reuse our new iconprifxed textfield type outside of signupform, it is doubtful whether it will ultimately improve our original code. After all, we don’t really simplify the implementation of the registration form. In fact, the calling method seems more complicated than before.

Let’s take some inspiration from swiftui’s own API design to see what it will look like if we change the text view configuration code to view extension.

extension View {
    func prefixedWithIcon(named name: String) -> some View {
        HStack {
            Image(systemName: name)
            self
        }
    }
}

The above code is very simple. We just expand the view to enrich the view combination. After extension is finished, we can use swiftui’s chain call method to configure the interface.

TextField("Username", text: $username)
                    .prefixedWithIcon(named: "person.circle.fill")

After the above operation, we can now add any SF symbols icon directly to the textfield view or any other view in swiftui

Let’s take a look at the complete code of the project

import SwiftUI

struct SignUpForm2: View {
    @State var username = ""
    @State var email = ""
    
    var body: some View {
        NavigationView{
            Form {
                Text ("intermediate level user registration interface"). Font (. Headline)
                
                TextField("Username", text: $username)
                    .prefixedWithIcon(named: "person.circle.fill")
                TextField("Email", text: $email)
                    .prefixedWithIcon(named: "envelope.circle.fill")
                
                Button(
                    action: { print("here") },
                    Label: {text ("submit")}
                )
            }. navigationbartitle (text ("user registration interface"))
        }
    }
}

Medium and advanced level

The intermediate level has carried out swiftui style transformation on the interface, and the middle and senior level players began to focus on the business logic. For example, verification of input. Let’s take a look at the moving picture below.

Four implementation schemes of IOS swiftui data form from rookie to expert

If we want to require that the user’s name must be greater than 5 digits, that the email must be in email format, and that the submit button will appear only when the above two conditions are met, how can we achieve this kind of slightly complicated effect?

There are many ways to realize it, but what is the most concise, flexible and efficient way?

The answer, of course, is swiftui style coding. Now I will lead you to achieve it!

The first step is to write a function to verify the mail

Personally, I prefer to use nspredict

func isValidEmail(_ email: String) -> Bool {
    let emailRegEx = "[A-Z0-9a-z._%+-][email protected][A-Za-z0-9.-]+\.[A-Za-z]{2,64}"

    let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
    return emailPred.evaluate(with: email)
}

The second step is to create a viewmodifier. When the user meets the conditions, the border will turn green

struct Validation<Value>: ViewModifier {
    var value: Value
    var validator: (Value) -> Bool
    
    func body(content: Content) -> some View {
        // Here we use Group to perform type erasure, to give our
        // method a single return type, as applying the 'border'
        // modifier causes a different type to be returned:
        Group {
            if validator(value) {
                content.border(Color.green)
            } else {
                content
            }
        }
    }
}

Main page

struct SignUpForm3: View {
    @State var username = ""
    @State var email = ""
    @State var uFlag = false
    @State var eFlag = false
    
    
    var body: some View {
        NavigationView{
            Form {
                Text ("user registration interface for middle and advanced level"). Font (. Headline)
                TextField("Username", text: $username)
                    .modifier(Validation(value: username) { name in
                        self.uFlag = name.count > 4
                        return self.uFlag
                        
                    })
                    .prefixedWithIcon(named: "person.circle.fill")
                
                TextField("Email", text: $email)
                    .modifier(Validation(value: email) { name in
                        self.eFlag = isValidEmail(name)
                        return self.eFlag
                        
                    })
                    .prefixedWithIcon(named: "envelope.circle.fill")
                
                if (self.uFlag && self.eFlag){
                Button(
                    action: {print("here")},
                    Label: {text ("submit")}
                )
                }
            }. navigationbartitle (text ("user registration interface"))
        }
    }
}

summary

This article takes you to appreciate the different thinking modes of different levels of players from rookies to experts when they complete the same task

  • Beginner mode

Simple use of hstack + image + textfield combination, fast implementation requirements. Thanks to the powerful functions of swiftui, the interface can reach the design level of apple

  • Entry level

The requirements of code neatness and code reuse are put forward, and the modular transformation of code is started to imitate swiftui.

  • intermediate level

Not satisfied with the simple modular transformation, we began to draw inspiration from the API design of swiftui and tried to use chain coding.

  • Medium and advanced level

Middle and senior players began to focus on business logic.

  • What about the top experts

Unfortunately, my level is limited, can only be introduced to the above level.


If you need the complete source code of the project, you can add my QQ.

QQ:3365059189
Swiftui technology exchange QQ group: 518696470

  • Please pay attention to my column icloudend, swiftui tutorial and source code

https://www.jianshu.com/c/7b3…