Learn to construct advanced varieties with my up to date assortment view view-model framework with out the wrestle utilizing Swift.
iOS
This technique just isn’t working, since cells within the type are going to be reused and this results in some inconsistency… please learn my different submit. 🤷♂️
CollectionView and enter varieties
My CollectionView framework simply bought a HUGE replace. There are many new adjustments, however one of many largest enchancment is the best way I take care of view fashions. Prior to now, you had to make use of lengthy perform names in your view mannequin together with the generic view & mannequin class names. When you’ve got ever learn my final UICollectionView information it is best to know what I am speaking about. Excellent news: I’ve a means higher resolution now! 😉
This replace not simply cleans up my code rather a lot, however it permits me so as to add customized view mannequin handlers, so I can work together with enter fields, toggles, and so forth. in a ridiculously straightforward means. One other large enchancment is that I began to make use of view identifiers. It was unintended discovery, I solely wished to search for another resolution for figuring out views by tags, then I had this sensible concept: why not search for cells by ids as properly?
Consequently I am now in a position to create varieties by utilizing the framework. I nonetheless consider that assortment views are the final word constructing blocks for many of the purposes. Yeah, you possibly can nonetheless say that there isn’t a silver bullet, however I am simply tremendous if this resolution can cowl 90% of the my use-cases. In any case, many of the apps are simply visualizing JSON knowledge in a pleasant, or not-so-nice means. 🤷♂️ #sarcasm
Reusable type elements
Let’s construct a type by utilizing the model new framework. Initially, you may have to combine it by utilizing a bundle supervisor. I actually hope that in a couple of weeks we are able to use Swift Package deal Supervisor, till than you it is best to go together with CocoaPods or carthage.
supply 'https://github.com/CoreKit/CocoaPods.git'
pod 'CollectionView', '~> 2.0.0'
github "CoreKit/CollectionView" "2.0.0"
Now let’s create a reusable cell for our enter fields. Be at liberty to make use of a xib file as common, the one distinction within the implementation goes to be that I take away the goal listener within the reset technique. We’ll add one in a while within the view-model. 🎯
import Basis
import CollectionView
class InputCell: Cell {
@IBOutlet weak var textField: UITextField!
override func reset() {
tremendous.reset()
self.textField.removeTarget(nil, motion: nil, for: .editingChanged)
}
}
I am additionally going to create a easy entity for displaying a placeholder if the shape discipline is empty and storing the precise worth of the enter discipline, let’s name this InputEntity
.
import Basis
struct InputEntity {
var placeholder: String
var worth: String?
}
Now the toughest half: making a connection between the view and the mannequin.
import Basis
import CollectionView
class InputViewModel: ViewModel<InputCell, InputEntity> {
var editingChangeHandler: ViewModelHandler?
override var peak: CGFloat {
return 60
}
override func updateView() {
self.view?.textField.placeholder = self.mannequin.placeholder
self.view?.textField.textual content = self.mannequin.worth
self.view?.textField.addTarget(self,
motion: #selector(self.editingChanged(_:)),
for: .editingChanged)
self.view?.textField.addTarget(self,
motion: #selector(self.editingDidEnd(_:)),
for: .editingDidEnd)
}
func onEditingChange(_ handler: @escaping ViewModelHandler) -> Self {
self.editingChangeHandler = handler
return self
}
@objc func editingChanged(_ textField: UITextField) {
self.mannequin.worth = textField.textual content
self.editingChangeHandler?(self)
}
@objc func editingDidEnd(_ textField: UITextField) {
print("nothing-to-do-here-now...")
}
}
It is fairly a fancy view mannequin, however it could actually do rather a lot as properly. The very first thing that it is best to perceive is the ViewModelHandler
which is mainly a generic alias that you may make the most of within the view fashions. It offers you the flexibility to cross across the type-safe view-model for the callbacks. You will see that in a while.
The second main change is the updateView
technique, which is used to replace the view based mostly on the info coming from the mannequin. I am additionally including my goal listeners to my view, so I can deal with person enter immediately contained in the view-model class.
The onEditingChange
technique is the “public” api of the view-model. I take advantage of the on
prefix now for including handlers, and listeners to my view-models. It mainly calls the saved block if a change occasion occurs. You’ll be able to add as many occasion handler blocks as you need. I actually hope that you will get the grasp of this method.
Yet one more factor: returning the the peak of the cell is a one-liner now! 🎊
Composing varieties and extra
The plan is for now to have an enter type with two enter fields. One for the e-mail deal with, the opposite goes for use for the password. The trick goes to be that this time I will not present you the complete code, however you must work out the remaining.
Nonetheless I am going to present you all the pieces that you will ever have to know to be able to make your individual varieties, even some advanced ones. Don’t be concerned, it is only a few strains of code.
import UIKit
import CollectionView
class ViewController: CollectionViewController {
override func viewDidLoad() {
tremendous.viewDidLoad()
let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 16), padding: .zero)
self.collectionView.supply = .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()
}
}
In the event you’ve ever labored with my assortment view framework, it is best to know that I all the time use a grid system, as a result of I do not actually wish to calculate numbers.
The supply is a set of view-models, grouped by sections. The one attention-grabbing half right here is that sources can now be initialized with an array of sections and view-models.
In the event you initialize a view-model with and identifier, in a while you possibly can question that one by the id. That is precisely whats taking place contained in the modifying change handler block. Each view-model has the flexibility to return another view-model by the id. View-models are type-safe by default, the viewModel handed contained in the block too, because of the generic ViewModelHandler
alias.
So on this little instance, when you sort one thing into the primary enter discipline, the very same textual content will seem within the second textual content discipline. You will get all of the view fashions by id while you want them. For instance if you must submit this type, you possibly can seize the e-mail and password fields by utilizing the identical method.
Constructing a login type
I problem you to construct a login type by yourself by utilizing my framework. I assure yout that it should not take greater than 30mins of labor. I am going to present you the ultimate view controller that I might use, so this would possibly offers you some assist.
If you wish to boost issues a bit of bit, you possibly can even add a checkbox for accepting the privateness coverage. The primary concept right here is that it is best to create reusable elements for each single merchandise in your type. So for instance a ToggleView with a corresponding view-model could be a superb method (additionally works for buttons). 🤫
Right here is the ultimate trace, you solely need to make your individual view-models and views…
import UIKit
import CollectionView
class ViewController: CollectionViewController {
enum Ids: String {
case electronic mail = "email-input"
case password = "password-input"
case privacyPolicy = "privacy-policy-checkbox"
case submit = "submit-button"
}
override func viewDidLoad() {
tremendous.viewDidLoad()
let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 16), padding: .zero)
self.collectionView.supply = .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 is it for now, an virtually full login type, with only a few strains of code. After all there may be an underlying framework, however when you verify the supply code, you may truly see that it comprises nothing that might be thought of as black magic. 💫