Preface
The parameters are encapsulated by Protocol, and the differences between parameters are smoothed out.
Today's article still revolves around a business scenario I encountered, and provides you with an idea - to use enum to smooth out the differences in array elements.
Business scenarios
Let me explain the business scenario first:
The page is a limited page that can be swiped (we will analyze later that it doesn’t matter whether it is infinite or limited)
Each subview of the page is bound to the data returned by the background, and its JSON can be roughly understood as the following form:
{ "aPart":{ "some":"" }, "bPart":[ { "label":"", "value":"" }, { "label":"", "value":"" } ], "cPart":[ { "iconUrl":"", "text":"", "functionType":"" }, { "iconUrl":"", "text":"", "functionType":"" } ], "dPart":{ "name":"", "age":"" }, "deviceType":"", "serviceAble":"" }
Business requirements are as follows:
- aPart corresponds to aView, bPart corresponds to bView, cPart corresponds to cView, and dPart corresponds to dView, so you can continue to be exhaustive
- The JSON structure of each part may be different
- When sending JSON in the background, different data will be returned according to the user's account. For example, if aPart's business does not have it, the background will return
"aPart":null
, the aView of the App needs to be hidden, and there may be cases where multiple parts may return to null, for example"bPart"
and"cPart"
If both are null, then the app's bView and cView need to be hidden.
So the question is, how to build an interface that can be flexibly configured on the iOS side?
What controls to use
Analysis using UIScrollView
As the first point in developing an App page, it is very important to select the right control, because the control determines the form of the final data source.
Because the backend data does not return a JSON array, and it is also limited data, many people will give priority to passingUIScrollView
I thought so at the beginning when I was building the page, but the trouble is this:
When sending JSON in the background, different data will be returned according to the user's account. For example, if aPart's business does not have it, the background will return"aPart":null
, the aView of the App needs to be hidden.
That is to say, youaView
、bView
、cView
、dView
After posting on the scrollView, you need to hide the page according to the data in the background, and even update the layout logic. Each view has 2 situations. Assuming that there are 4 business data in the background, then that is to the power of 2-4—32 possibilities.
In fact, it is very simple to hide or display, but if you use itSnapKit
Layout, then updating the layout of the view may not be particularly good. At the same time, if there is new business data on this page in the future, the subview will continue to increase and the maintenance cost will become higher and higher.
So, don’t veto the solution of using UIScrollView here, it doesn’t mean that it cannot be built, but that it is a bit expensive and not flexible enough.
So the only option left is to use the UI bound through the data sourceUITableView
. (Note: Actually useUICollectionView
andUITableView
They are all the same, just one more waterfall flow layout).
Analysis using UITableView
useUITableView
Advantages:
The page is driven entirely through data, and whether the page is displayed is determined entirely by whether there is data or not.
However, data sources have a stricter data format than ordinary models - arrays!
And each element of the array is preferably the same data type, because if using[Any]
This way, the cost is too high.
OK, since we have decided to use itUITableView
If you do page construction, the remaining difficulties will come - how to process the background data into a useful data source?
Processing data
It is not difficult to process background data into an array, the key is that system and data types are the difficulties.
In the previous article, I went to the coordination through ProtocolModel
and[String: String]
, Is it feasible under this situation?
protocol EraserConvertible {} struct Response: Codabel { let aPart: APart? let bPart: BPart? let cPart: CPart? let dPart: DPart? } struct APart: Codabel, EraserConvertible {} struct BPart: Codabel, EraserConvertible {} struct CPart: Codabel, EraserConvertible {} struct DPart: Codabel, EraserConvertible {}
Let’s look back at this JSON, you have to recognize this reality. Each part is independent and there is no correlation at all. If you want to be consistent in type,EraserConvertible
This agreement is difficult to satisfy.
It is better to do upward type coordination, that is, the data source becomeslet dataSource = [Response]()
In this form, single data is obtained in the tableView's data source method and then analyzed:
let dataSource = [Response]() func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item = dataSource[] if let aPart = { /// Build aCell let cell = (withIdentifier: )! /// Assign aPart to aCell = aPart return cell } if let bPart = { /// Build bCell, let cell = (withIdentifier: )! /// Assign bPart to bCell = bPart return cell } . . . }
Well, this looks very good, but each item contains aPart to dPart, and the solution is feasible. Let’s think about whether there are more elegant ideas?
Different types of data sources determine different cells. Can we use state to represent it?
So I wrote down the code like this:
enum BusinessPart { case a case b case c case d }
The data source becomeslet dataSource = [BusinessPart]()
This form, how to distinguish eachcase
What about different data?
Swift's enum can take parameters, and a single case does not take parameters, so it is very free to take any type of parameters.
So we continued to transformBusinessPart
:
enum BusinessPart { case a(APart) case b(BPart) case c(CPart) case d(DPart) }
That way I'll pass nowenum
The difference between the smoothed array elements, then just form the background data frame into the [BusinessPart] format I want. Here is the processing logic:
private func process(model: Response) -> [BusinessPart] { var array: [BusinessPart] = [] /// Add the array only if it is not empty, and the background returns null. At this time, aPart is nil, then aCell will not appear. if let aPart = { (.a(aPart)) } if let bPart = { (.b(bPart)) } if let cPart = { (.c(cPart)) } if let dPart = { (.d(dPart)) } return array }
existTableView
The data source method is used in this way:
let dataSource = [BusinessPart]() func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item: BusinessPart = dataSource[] switch item { case .a(let aPart): /// Build aCell let cell = (withIdentifier: )! /// Assign aPart to aCell = aPart return cell case .b(let bPart): /// Build bCell let cell = (withIdentifier: )! /// Assign bPart to bCell = bPart return cell case .c(let cPart): /// Build cCell let cell = (withIdentifier: )! /// Assign cPart to cCell = cPart return cell . . . } }
As for this[BusinessPart]
How to use dataSourceUITableViewDataSource
How to deal with it, or inBusinessPart
How to deal with it in the classification is a question of whether one has a different opinion.
Summarize
In the end, the data processing of this page finally became a unified thinking of upward or downward.
In fact, it is a problem of how to integrate a data with particularly large differences into a suitable array in iOS, and achieve the integration of the model through full coverage of fields, or by enumerating the characteristics with parameters in Swift to smooth out the differences.
From a code level, the code volume of the two is similar, but using enum to smooth out the differences in array elements provides us with a new solution to the problem, at least this is the result of my own thinking.
Another angle is to expand it through layout controls, because I see Android directly uses a ScrollView:
In Android development, most controls have the visibility attribute, and three attributes are "visible", "invisible", and "gone". It is mainly used to set the display and hide the control controls.
invisible When the control's visibility property is invisible, the interface retains the space occupied by the view control; and when the control property is gone, the interface does not retain the space occupied by the view control.
Because the three attributes of Android can be easily hidden or not, and whether to retain the space occupied by the control.
iOS may need to not only change the isHidden attribute, but also change the frame of the view, which is too expensive. Analysis using UITableView has been mentioned.
But it is not impossible, for exampleFlexLibIt should be possible. (Note: I have never used the FlexLib library myself, this needs to be verified 😁), but it has also increased a certain learning cost and introduced more libraries.
Reference Documents
Swift: Can you use enum?
The above is the detailed explanation of the example of Swift using enum to smooth array elements. For more information about Swift enum to smooth array elements, please pay attention to my other related articles!