قالب وردپرس درنا توس
Home / IOS Development / Embed forms for iOS apps

Embed forms for iOS apps



Learn how to build complex forms with my up-to-date collection view, no-fight display model using Swift.


CollectionView and input forms

My CollectionView framework has just received a major update. There are many new changes, but one of the biggest improvements is the way I look at display models. Previously, you had to use long-function names in the display model, including generic display and model class names. If you've ever read my ultimate UICollectionView guide, you should know what I'm talking about. Good news: I have a better solution now! 😉

This update not only cleans my code, but it allows me to add custom display models so I can interact with input fields, switches, etc. In a ridiculously easy way. Another big improvement is that I started using display identifiers. It was random discovery, I just wanted to look for an alternative solution to identify impressions with codes, then I had this brilliant idea: why not look up cells of ids too?

As a result, I am now able to create forms using the frame. I still believe that collection views are the ultimate building blocks for most applications. Yes, you can still say that it is not silver ball, but it is only good if this solution can cover 90% of my boxes. After all, most of the apps simply visualize JSON data in a nice, or not so nice way. 🤷♂️ #sarcasm


Reusable Form Components

Let's build a form using the brand new framework. First of all, you need to integrate it by using a packet processing. I really hope that within a few weeks we can use the Swift Package Manager until you should join CocoaPods or carthage.

  # cocoapods
source & # 39; https: //github.com/CoreKit/CocoaPods.git'
pod & # 39; CollectionView & # 39 ;, & # 39; ~> 2.0.0 & # 39;

# carthage
github "CoreKit / CollectionView" "2.0.0"

Now let's create a reusable cell for our fields. Feel free to take a xib file as usual, the only difference in the implementation is that I remove the target listener in the reset method. We add one later in the display model. 1

9

  import fund
Import CollectionView

class InputCell: Cell {

@IBOutlet weak was textField: UITextField!

override func reset () {
super.reset ()

self.textField.removeTarget (null, action: null, for: .editingChanged)
}
}

I will also create a simple device to display a placeholder if the form field is empty and stores the actual value of the input field, let's call it InputEntity .

  Import Foundation

struct InputEntity {
Be placeholder: String
was value: strict?
}

Now, the most difficult part is: Connection between the display and the model.

  import fund
Import CollectionView

class InputViewModel: ViewModel  {

was editingChangeSeller: ViewModelHandler?

override was height: CGFloat {
return 60
}

override func updateView () {
self.view? .textField.placeholder = self.model.placeholder
self.view? .textField.text = self.model.value

self.view? .textField.addTarget (self,
action: #selector (self.editingChanged (_ :)),
for: .editingChanged)
self.view? .textField.addTarget (self,
action: #selector (self.editingDidEnd (_ :)),
for: .editingDidEnd)
}

func onEditingChange (_ trades: @escaping ViewModelHandler) -> Even {
self.editingChangeHandler = trades
return yourself
}

@objc func editingChanged (_ textField: UITextField) {
self.model.value = textField.text
self.editingChangeHandler? (own-)
}

@objc func editingDidEnd (_ textField: UITextField) {
print ("nothing-to-do-here-now ...")
}
}

It is quite a complex display model, but it can also do a lot. The first thing you should understand is ViewModelHandler which is basically a generic alias that you can use in the display models. It gives you the opportunity to pass around the type-secure display model of the callbacks. You see it later.

The second major change is the method updateView which is used to update the view based on the data coming from the model. I also add the target listeners to my view so I can handle user input directly in the display model class.

onEditingChange is "public" api of the display model. I am using on the prefix now to add handle and listen to my display models. It is basically called the saved block if a change event occurs. You can add as many event handling blocks as you like. I really hope you get this approach.

One more thing: returning the height of the cell is a one-liner now! 19


Compose Forms and More

The plan is for now to have an entry form with two entry fields. One for the email address, the other is used for the password. The trick is going to be that this time I won't show you the whole code, but you have to figure out the rest.

However, I want to show you everything you need to know in order to create your own shapes, even some complex ones. Don't worry, there are only a few lines of code.

  import UIKit
Import CollectionView

class ViewController: CollectionViewController {

override func viewDidLoad () {
super.viewDidLoad ()

la grid = Grid (columns: 1, margin: UIEdgeInsets (all: 16), padding: .zero)
self.collectionView.source = .init (grid: grid, [
            [
                InputViewModel(id: "email-input", .init(placeholder: "Email", value: nil))
                .onEditingChange { viewModel in
                    guard let passwordViewModel = viewModel.by(id: "password-input") as? InputViewModel else {
                        return
                    }
                    passwordViewModel.model.value = viewModel.model.value ?? ""
                    passwordViewModel.updateView()
                },
                InputViewModel(id: "password-input", .init(placeholder: "Password", value: nil)),
            ],
])
self.collectionView.reloadData ()
}
}

If you have ever worked with the public viewing connection, you should know that I always use a grid system because I do not like to figure out numbers.

The source is a set of display models, grouped by sections. The only interesting part here is that sources can now be initialized with a number of sections and display models.

If you initialize a display model and identify, you can later ask one by one. This is exactly what is happening in the editing manager management block. Each display model has the ability to return another display model with ID. Display models are by default safe, but also viewModel passed inside the block, thanks to the generic ViewModelHandler alias.

So in this small example, if you type something into the first input field, the exact same text appears in the second text field. You can get all the display models by id when you need them. For example, if you have to submit this form, you can grab the email addresses and password fields using the same approach.


Creating a Login Form

I challenge you to create a login form alone using my frames. I guarantee that it will not take more than 30 minutes of work. I want to show you the last view control I want to use, so this can give you some help.

If you want to spice things up, you can even add a checkbox to accept the privacy policy. The main idea here is that you should create reusable components for each item in your form. So, for example, a ToggleView with a similar display model would be a good approach (also works for buttons). 19

Here's the last hint, you just have to create your own display models and views …

  import UIKit
Import CollectionView

class ViewController: CollectionViewController {

enum Ids: String {
case email = "email-input"
case password = "password input"
case privacyPolicy = "privacy policy checkbox"
case submit = "submit-button"
}

override func viewDidLoad () {
super.viewDidLoad ()

la grid = Grid (columns: 1, margin: UIEdgeInsets (all: 16), padding: .zero)
self.collectionView.source = .init (grid: grid, [
            [
                InputViewModel(id: Ids.email.rawValue, .init(placeholder: "Email", value: nil))
                .onEditingEnd { viewModel in
                    guard let passwordViewModel = viewModel.by(id: Ids.password.rawValue) as? InputViewModel else {
                        return
                    }
                    passwordViewModel.view?.textField.becomeFirstResponder()
                },
                InputViewModel(id: Ids.password.rawValue, .init(placeholder: "Password", value: nil, secure: true))
                .onEditingEnd { viewModel in
                    viewModel.view?.textField.endEditing(true)
                },
            ],
[
                ToggleViewModel(id: Ids.privacyPolicy.rawValue, .init(label: "Privacy policy", value: false))
                .onValueChange { viewModel in
                    guard let submitViewModel = viewModel.by(id: Ids.submit.rawValue) as? ButtonViewModel else {
                        return
                    }
                    var model = submitViewModel.model
                    model.enabled = viewModel.model.value
                    submitViewModel.model = model
                    submitViewModel.updateView()
                },
            ],
[
                ButtonViewModel(id: Ids.submit.rawValue, .init(title: "Submit", enabled: false))
                .onSubmit { viewModel in
                    guard
                        let emailViewModel = viewModel.by(id: Ids.email.rawValue) as? InputViewModel,
                        let passwordViewModel = viewModel.by(id: Ids.password.rawValue) as? InputViewModel
                    else {
                        return
                    }
                    /* ... */
                },
            ],
])
self.collectionView.reloadData ()
}
}

That's it for now, an almost complete login form, with just a few lines of code. Of course, it is an underlying framework, but if you check the source code, you will actually see that it contains nothing that can be considered black magic.

What do you think of this approach? Do you like it, do you have ideas on how to improve it? You are welcome to share your thoughts on discord or through twitter . If you do not, please do not forget to subscribe to my monthly newsletter below.




Source link