100 Days of Swift UI – Day 18
WeSplit – Part 3
Review
- SwiftUI allows no more than 10 child views inside each parent. If we want to add more we should place our views inside groups.
- Format specifiers let us control the way numbers are shown inside string interpolation. They use a rather dated string format that comes from C, but they are useful sometimes.
- Segmented controls are created using picker views in SwiftUI. Picker adopt segmented styling when we apply the
.pickerStyle(SegmentedPickerStyle())
modifier on them. - Forms can scroll, so it’s a good idea to check our content doesn’t overlap the system clock by accident.
- The
keyboardType()
modifier lets us change the keyboard that is shown when a text field is active. - When creating a text field we need to provide some placeholder text. This helps users see what should be entered, and is also useful to screen readers.
- All SwiftUI views must have a
body
property. Thisbody
must always return precisely one view. That view might contain more views inside it, but you still need to return precisely one from the computed property. - We can send a string to the
navigationBarTitle()
modifier to place a title at the top of our navigation view. We should always attach this modifier to the view inside the navigation view rather than the navigation view itself! - If we want to modify a property, we need to use a SwiftUI property wrapper such as
@State
.@State
tells SwiftUI to store the value somewhere that can be changed freely, outside of our struct. ForEach
views let us loop over ranges and arrays. They are used to create many instances of something very quickly.- Whenever an
@State
property changes, Swift re-invokes ourbody
property. This behaviour forces all values inside the body to be re-evaluated, making sure they are updated for the changes. - SwiftUI’s previews aren’t included in our app if we send it to the App Store. Swift automatically strips out the previews; they are just there to help us design our UI.
Challenges
Challenge 1: Add a header to the third section, saying “Amount per person”.
Next to the Section
add the code (header: Text("Amount per person"))
, imitating the code in the second Section.
Challenge 2: Add another section showing the total amount for the check – i.e., the original amount plus tip value, without dividing by the number of people.
I found, with my greatest dismay, that performing calculations inside views is quite hard, if not straight impossible. I tried to use the $
sign when I thought it would have been useful but it was not compiling.
My solution, for now, was to create another computer property:
var grandTotal: Double {
let orderAmount = Double(checkAmount) ?? 0
let tipSelection = Double(tipPercentages[tipPercentage])
let tipValue = orderAmount / 100 * tipSelection
return tipValue + orderAmount
}
…and use its value inside of a Section element:
Section(header: Text("Total amount")) {
Text("$\(grandTotal, specifier: "%.2f")")
}
I’m not completely sure this is the best way, but performing calculations inside that Text was throwing errors, especially because it was using the non-State version of a property. Adding $
in front, though, didn’t solve the issue so I went back and wrote a computer property. If you have a better solution, please share it here below, or just comment what you would have changed.
Challenge 3: Change the “Number of people” picker to be a text field, making sure to use the correct keyboard type.
It seems I cannot do this …
I tried to create a TextField, but it wants a string for the placeholder text and a binding string. I changed all the necessary things to make it compile but I cannot have it write “2 people”, just the number 2. If the text field is empty, the total amount will read NaN, that is “Not a Number”, which we don’t want.
I am really angry that auto-completion is so slow, so cryptic when it eventually shows up and that the documentation is completely lacking … I miss UIKit, really … this doesn’t feel like something I want to face for the rest of my SwiftUI learning days … a text field should just be … A TEXT FIELD! I should be able to convert data types one into the other, not this crazy thing…
In UIKit I would make an alert controller come out of the text field if this had not to be edited directly, but how can I do this here? Damn it!
One way this is working is this:
- change the
numberOfPeople
variable to have a value of"2"
so that it is a String. - in the
totalPerPerson
computed property, change the first line tolet peopleCount = Double(numberOfPeople) ?? 1
. The 1 is to avoid the NaN, not that I consider this a viable solution, it is just looking awful … - in the
body
, replace the Picker with a TextField with “Number of people” as placeholder text and$numberOfPeople
for the binding string. I then set the.keyboardType(.decimalPad)
.
Again, I do not think this is the best solution, far from it …
I hope in the next days to go back to UIKit and start again enjoying coding… all this lack of clarity is really unnerving. What is the point of having a “declarative” interface framework if then I cannot know how it works? How did people learn how to change the style of a picker if it is not documented anywhere what types are at our disposal?
As Aleksandar Vacić said this morning: for beginners, SwiftUI is terrible. I could not agree more.
That’s it for today.
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!