This app is going to allow user input with a date picker and two steppers, which combined will tell us when they want to wake up, how much sleep they usually like, and how much coffee they drink.
So, please start by adding three properties that let us store the information for those controls:
@State private var wakeUp = Date.now
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 1
Inside our body
we’re going to place three sets of components wrapped in a VStack
and a NavigationView
, so let’s start with the wake up time. Replace the default “Hello World” text view with this:
NavigationView {
VStack {
Text("When do you want to wake up?")
.font(.headline)
DatePicker("Please enter a time", selection: $wakeUp, displayedComponents: .hourAndMinute)
.labelsHidden()
// more to come
}
}
We’ve asked for .hourAndMinute
configuration because we care about the time someone wants to wake up and not the day, and with the labelsHidden()
modifier we don’t get a second label for the picker – the one above is more than enough.
Next we’re going to add a stepper to let users choose roughly how much sleep they want. By giving this thing an in
range of 4...12
and a step of 0.25 we can be sure they’ll enter sensible values, but we can combine that with the formatted()
method so we see numbers like “8” and not “8.000000”.
Add this code in place of the // more to come
comment”
Text("Desired amount of sleep")
.font(.headline)
Stepper("\(sleepAmount.formatted()) hours", value: $sleepAmount, in: 4...12, step: 0.25)
Finally we’ll add one last stepper and label to handle how much coffee they drink. This time we’ll use the range of 1 through 20 (because surely 20 coffees a day is enough for anyone?), but we’ll also display one of two labels inside the stepper to handle pluralization better. If the user has set a coffeeAmount
of exactly 1 we’ll show “1 cup”, otherwise we’ll use that amount plus “cups”, all decided using the ternary conditional operator.
Add these inside the VStack
, below the previous views:
Text("Daily coffee intake")
.font(.headline)
Stepper(coffeeAmount == 1 ? "1 cup" : "\(coffeeAmount) cups", value: $coffeeAmount, in: 1...20)
The final thing we need is a button to let users calculate the best time they should go to sleep. We could do that with a simple button at the end of the VStack
, but to spice up this project a little I want to try something new: we’re going to add a button directly to the navigation bar.
First we need a method for the button to call, so add an empty calculateBedtime()
method like this:
func calculateBedtime() {
}
Now we need to use the toolbar()
modifier to add a trailing button to the navigation view. We used this previously along with ToolbarItemGroup
to place a button next to the keyboard, but here our needs are much simpler: we just want a single button in the navigation bar, which can be done by adding a button directly to the toolbar.
While we’re here, we might as well also use navigationTitle()
to put some text at the top.
So, add these modifiers to the VStack
:
.navigationTitle("BetterRest")
.toolbar {
Button("Calculate", action: calculateBedtime)
}
Tip: Our button will automatically be placed in the top-right corner for left-to-right languages such as English, but will automatically move to the other side for right-to-left languages.
That won’t do anything yet because calculateBedtime()
is empty, but at least our UI is good enough for the time being.
SPONSORED Let’s face it, SwiftUI previews are limited, slow, and painful. Judo takes a different approach to building visually—think Interface Builder for SwiftUI. Build your interface in a completely visual canvas, then drag and drop into your Xcode project and wire up button clicks to custom code. Download the Mac App and start your free trial today!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.