[ad_1]
This methodology shouldn’t be 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 kinds
My CollectionView framework simply received a HUGE replace. There are many new adjustments, however one of many largest enchancment is the way in which I take care of view fashions. Up to now, you had to make use of lengthy operate names in your view mannequin together with the generic view & mannequin class names. You probably have ever learn my final UICollectionView information you need to know what I am speaking about. Excellent news: I’ve a means higher answer now! 😉
This replace not simply cleans up my code so much, but it surely permits me so as to add customized view mannequin handlers, so I can work together with enter fields, toggles, and so on. in a ridiculously simple means. One other big enchancment is that I began to make use of view identifiers. It was unintended discovery, I solely needed to search for an alternate answer for figuring out views by tags, then I had this sensible concept: why not lookup cells by ids as nicely?
Consequently I am now capable of create kinds by utilizing the framework. I nonetheless consider that assortment views are the last word constructing blocks for many of the purposes. Yeah, you’ll be able to nonetheless say that there is no such thing as a silver bullet, however I am simply positive if this answer 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. To begin with, you will must combine it by utilizing a package deal supervisor. I actually hope that in just a few weeks we are able to use Swift Package deal Supervisor, till than you you need to go together with CocoaPods or carthage.
# cocoapods
supply 'https://github.com/CoreKit/CocoaPods.git'
pod 'CollectionView', '~> 2.0.0'
# carthage
github "CoreKit/CollectionView" "2.0.0"
Now let’s create a reusable cell for our enter fields. Be happy to make use of a xib file as normal, the one distinction within the implementation goes to be that I take away the goal listener within the reset methodology. We’ll add one afterward 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 subject is empty and storing the precise worth of the enter subject, 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 top: 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 do so much as nicely. The very first thing that you need to perceive is the ViewModelHandler which is mainly a generic alias which you could make the most of within the view fashions. It provides you the flexibility to move across the type-safe view-model for the callbacks. You will see that afterward.
The second main change is the updateView methodology, which is used to replace the view based mostly on the information coming from the mannequin. I am additionally including my goal listeners to my view, so I can deal with person enter instantly contained in the view-model class.
The onEditingChange methodology is the “public” api of the view-model. I exploit 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 may add as many occasion handler blocks as you need. I actually hope that you’re going to get the dangle of this strategy.
Yet another factor: returning the the peak of the cell is a one-liner now! 🎊
Composing kinds and extra The plan is for now to have an enter type with two enter fields. One for the e-mail handle, 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 it’s a must to work out the remaining.
Nevertheless I will present you the whole lot that you’re going to ever must know with the intention to make your personal kinds, even some advanced ones. Don’t be concerned, it is just some traces 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()
}
}
If you happen to’ve ever labored with my assortment view framework, you need to know that I at all times use a grid system, as a result of I do not actually prefer 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.
If you happen to initialize a view-model with and identifier, afterward you’ll be able to question that one by the id. That is precisely whats occurring contained in the enhancing 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, if you happen to kind one thing into the primary enter subject, the very same textual content will seem within the second textual content subject. You will get all of the view fashions by id if you want them. For instance if it’s a must to submit this type, you’ll be able to seize the e-mail and password fields by utilizing the identical strategy.
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 will present you the ultimate view controller that I’d use, so this may provides you some assist.
If you wish to boost issues a bit of bit, you’ll be able to even add a checkbox for accepting the privateness coverage. The principle concept right here is that you need to create reusable elements for each single merchandise in your type. So for instance a ToggleView with a corresponding view-model could be a great strategy (additionally works for buttons). 🤫
Right here is the ultimate trace, you solely must make your personal 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 nearly full login type, with just some traces of code. In fact there’s an underlying framework, however if you happen to examine the supply code, you will really see that it accommodates nothing that might be thought of as black magic. 💫
[ad_2]